Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 高級開發 >> Android API學習 SoundPool 和 MediaPlayer

Android API學習 SoundPool 和 MediaPlayer

編輯:高級開發

android平台中關於音頻播放有以下兩種方式:

  1. SoundPool —— 適合短促且對反應速度比較高的情況(游戲音效或按鍵聲等)

  2. MediaPlayer —— 適合比較長且對時間要求不高的情況

  -------------------------------------------------------------------------------------------

  SoundPool

  1. 創建一個SoundPool

  public SoundPool(int maxStream, int streamType, int srcQuality)

  maxStream —— 同時播放的流的最大數量

  streamType —— 流的類型,一般為STREAM_MUSIC(具體在AudioManager類中列出)

  srcQuality —— 采樣率轉化質量,當前無效果,使用0作為默認值

  eg.

  SoundPool soundPool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);

  創建了一個最多支持3個流同時播放的,類型標記為音樂的SoundPool。

  2. 加載音頻資源

  可以通過四種途徑來記載一個音頻資源:

  int load(AssetFileDescriptor afd, int priority)

  通過一個AssetFileDescriptor對象

  int load(Context context, int resId, int priority)

  通過一個資源ID

  int load(String path, int priority)

  通過指定的路徑加載

  int load(FileDescriptor fd, long offset, long length, int priority)

  通過FileDescriptor加載

  *API中指出,其中的priority參數目前沒有效果,建議設置為1。

  一個SoundPool能同時管理多個音頻,所以可以通過多次調用load函數來記載,如果記載成功將返回一個非0的soundID ,用於播放時指定特定的音頻。

  eg.

  int soundID1 = soundPool.load(this, R.raw.sound1, 1);

  if(soundID1 ==0){

  // 記載失敗

  }else{

  // 加載成功

  }

  int soundID2 = soundPool.load(this, R.raw.sound2, 1);

  ...

  這裡加載了兩個流,並分別記錄了返回的soundID 。

  需要注意的是,

  流的加載過程是一個將音頻解壓為原始16位PCM數據的過程,由一個後台線程來進行處理異步,所以初始化後不能立即播放,需要等待一點時間。

  3. 播放控制

  接上頁

  有以下幾個函數可用於控制播放:

  final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)

  播放指定音頻的音效,並返回一個streamID 。

  priority —— 流的優先級,值越大優先級高,影響當同時播放數量超出了最大支持數時SoundPool對該流的處理;

  loop —— 循環播放的次數,0為值播放一次,-1為無限循環,其他值為播放loop+1次(例如,3為一共播放4次).

  rate —— 播放的速率,范圍0.5-2.0(0.5為一半速率,1.0為正常速率,2.0為兩倍速率)

  final void pause(int streamID)

  暫停指定播放流的音效(streamID 應通過play()返回)。

  final void resume(int streamID)

  繼續播放指定播放流的音效(streamID 應通過play()返回)。

  final void stop(int streamID)

  終止指定播放流的音效(streamID 應通過play()返回)。

  這裡需要注意的是,

  1.play()函數傳遞的是一個load()返回的soundID——指向一個被記載的音頻資源 ,如果播放成功則返回一個非0的streamID——指向一個成功播放的流 ;同一個soundID 可以通過多次調用play()而獲得多個不同的streamID (只要不超出同時播放的最大數量);

  2.pause()、resume()和stop()是針對播放流操作的,傳遞的是play()返回的streamID ;

  3.play()中的priority參數,只在同時播放的流的數量超過了預先設定的最大數量是起作用,管理器將自動終止優先級低的播放流。如果存在多個同樣優先級的流,再進一步根據其創建事件來處理,新創建的流的年齡是最小的,將被終止;

  4.無論如何程序退出時,手動終止播放並釋放資源是必要的。

  eg.

  //這裡對soundID1的音效進行播放——優先級為0(最低),無限循環,正常速率。

  int streamID = soundPool.play(soundID1 , 1.0, 1.0, 0, -1, 1.0);

  if(streamID ==0){

  // 播放失敗

  }else{

  // 播放成功

  }

  ...

  // 暫停soundID1的播放

  soundPool.pause(streamID );

  ...

  // 恢復soundID1的播放

  soundPool.resume(streamID );

  ...

  // 終止播放,記住循環為-1時必須手動停止

  接上頁

  soundPool.stop(streamID );

  *API中指出,即使使用無效的soundID /streamID (操作失敗或指向無效的資源)來調用相關函數也不會導致錯誤,這樣能減輕邏輯的處理。

  4. 更多屬性設置

  其實就是paly()中的一些參數的獨立設置:

  final void setLoop(int streamID, int loop)

  設置指定播放流的循環.

  final void setVolume(int streamID, float leftVolume, float rightVolume)

  設置指定播放流的音量.

  final void setPriority(int streamID, int priority)

  設置指定播放流的優先級,上面已說明priority的作用.

  final void setRate(int streamID, float rate)

  設置指定播放流的速率,0.5-2.0.

  5. 釋放資源

  可操作的函數有:

  final boolean unload(int soundID)

  卸載一個指定的音頻資源.

  final void release()

  釋放SoundPool中的所有音頻資源.

  -匯總-

  一個SoundPool可以:

  1.管理多個音頻資源,通過load()函數,成功則返回非0的soundID;

  2.同時播放多個音頻,通過play()函數,成功則返回非0的streamID;

  3.pause()、resume()和stop()等操作是針對streamID(播放流)的;

  4.當設置為無限循環時,需要手動調用stop()來終止播放;

  5.播放流的優先級(play()中的priority參數),只在同時播放數超過設定的最大數時起作用;

  6.程序中不用考慮(play觸發的)播放流的生命周期,無效的soundID/streamID不會導致程序錯誤。

  -------------------------------------------------------------------------------------------

  MediaPlayer

  你可以通過new或便捷的靜態create函數組來創建一個MediaPlayer對象。

  兩種方式的比較:

  new MediaPlayer()

  1.成功調用後,MediaPlayer將處於Idle狀態;

  2.setDataSource提供了對String(path)、Uri和FileDescriptor格式的資源路徑的支持;

  3.後續需要手動調用prepare()才能進行播放。

  MediaPlayer.create(...)

  1.成功調用後,MediaPlayer將處於Prepared狀態;

  2.create提供了對int(resID)和Uri格式的資源路徑的支持;

  接上頁

  3.無需(也不能)再次調用prepare()就能直接播放。

  MediaPlayer的狀態圖:

  橢圓 代表一個MediaPlayer可能處於的狀態。

  圓弧 代表(驅動對象狀態轉變的)播放控制的操作。

  >>箭頭有兩種形態:

  單箭 頭代表同步函數的調用;

  雙箭 頭代表異步的函數調用。

  函數在不同狀態下的有效性:

  Method Name Valid Sates Invalid States Comments

  attachAuxEffect{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}{Idle, Error}This method must be called after setDataSource. Calling it does not change the object state.

  getAudiOSessionIdany{}This method can be called in any state and calling it does not change the object state.

  getCurrentPosition{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}{Error}Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the Error state.

  getDuration{Prepared, Started, Paused, Stopped, PlaybackCompleted}{Idle, Initialized, Error}Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the Error state.

  getVideoHeight{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}{Error}Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the Error state.

  getVideoWidth{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}{Error}Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the Error state.

  isPlaying{Idle, Initialized, Prepared, Started, Paused, Stopped,

  接上頁

PlaybackCompleted}{Error}Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the Error state.

  pause{Started, Paused}{Idle, Initialized, Prepared, Stopped, PlaybackCompleted, Error}Successful invoke of this method in a valid state transfers the object to the Paused state. Calling this method in an invalid state transfers the object to theError state.

  prepare{Initialized, Stopped}{Idle, Prepared, Started, Paused, PlaybackCompleted, Error}Successful invoke of this method in a valid state transfers the object to the Prepared state. Calling this method in an invalid state throws an IllegalStateException.

  prepareAsync{Initialized, Stopped}{Idle, Prepared, Started, Paused, PlaybackCompleted, Error}Successful invoke of this method in a valid state transfers the object to the Preparing state. Calling this method in an invalid state throws an IllegalStateException.

  releaseany{}After release() , the object is no longer available.

  reset{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}{}After reset() , the object is like being just created.

  seekTo{Prepared, Started, Paused, PlaybackCompleted}{Idle, Initialized, Stopped, Error}Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the Error state.

  setAudiOSessionId{Idle}{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}This method must be called in idle state as the audio session ID must be known before calling setDataSource. Calling it does not change the object state.

  setAudiOStreamType{Idle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted}{Error}Successful invoke of this method does not change the state. In order for the target audio stream type to become effective, this

  接上頁

method must be called before prepare() or prepareAsync().

  setAuxEffectSendLevelany{}Calling this method does not change the object state.

  setDataSource{Idle}{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}Successful invoke of this method in a valid state transfers the object to the Initialized state. Calling this method in an invalid state throws an IllegalStateException.

  setDisplayany{}This method can be called in any state and calling it does not change the object state.

  setLooping{Idle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted}{Error}Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the Error state.

  isLoopingany{}This method can be called in any state and calling it does not change the object state.

  setOnBufferingUpdateListenerany{}This method can be called in any state and calling it does not change the object state.

  setOnCompletionListenerany{}This method can be called in any state and calling it does not change the object state.

  setOnErrorListenerany{}This method can be called in any state and calling it does not change the object state.

  setOnPreparedListenerany{}This method can be called in any state and calling it does not change the object state.

  setOnSeekCompleteListenerany{}This method can be called in any state and calling it does not change the object state.

  setScreenOnWhilePlayingany{}This method can be called in any state and calling it does not change the object state.

  setVolume{Idle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted}{Error}Successful invoke of this method does not change the state.

  setWakeModeany{}This method can be called in any state and calling it does not change the object state.

  接上頁

  start{Prepared, Started, Paused, PlaybackCompleted}{Idle, Initialized, Stopped, Error}Successful invoke of this method in a valid state transfers the object to the Started state. Calling this method in an invalid state transfers the object to theError state.

  stop{Prepared, Started, Stopped, Paused, PlaybackCompleted}{Idle, Initialized, Error}Successful invoke of this method in a valid state transfers the object to the Stopped state. Calling this method in an invalid state transfers the object to theError state.

  要點:

  1.如果由於錯誤的操作(參照上圖)導致MediaPlayer處於Error狀態,可通過reset()函數來使其恢復到Idle狀態,再重新執行setDataSource等初始化操作(ps:如果是通過create函數綁定資源ID創建的就郁悶了...);

  2.API中指出雖然reset後的MediaPlayer就像相當於新new的一樣,但存在微妙的差異的:

  在這兩種情況下播放器處於Idle狀態,此時調用getCurrentPosition(), getDuration(),getVideoHeight(),getVideoWidth(), setAudiOStreamType(int),setLooping(boolean), setVolume(float, float), pause(), start(), stop(),seekTo(int), prepare() 或 prepareAsync() 等函數都屬與編程錯誤。當在MediaPlayer剛創建後調用這些函數,用戶指定的OnErrorListener.onError() 回調函數不會被internal player engine(內部播放引擎)調用,並且播放器的狀態依然未變;但如果是在調用reset() 函數之後,用戶指定的OnErrorListener.onError() 回調函數將會被internal player engine(內部播放引擎)調用,並且播放器的狀態將轉變為Error(錯誤)狀態。

  3.使用完畢後應該立即調用release()函數來釋放資源,如果操作成功,MediaPlayer對象將處於End狀態,此時無法再進行任何操作,除非重新創建MediaPlayer對象。

  更多的細節通過一個用new方式來創建的示例說明:

  Java代碼

  // 通過new創建後的player處於Idle狀態

  MediaPlayer mp = new MediaPlayer();

  if(mp==null){

  // new創建有可能會返回null值,檢測是好的習慣

  return;

  }

  // 設置資源路徑,成功執行的話player將處於Initialized狀態

  try {

  接上頁

  mp.setDataSource("/sdcard/test.mp3"); // 直接傳URL也是可以的,將自動處理緩沖

  } catch (IllegalArgumentException e) {

  e.printStackTrace();

  } catch (IllegalStateException e) {

  // 如果在非Idle狀態下調用setDataSource就會導致該異常

  e.printStackTrace();

  } catch (IOException e) {

  e.printStackTrace();

  }

  // 設置必要的監聽器

  mp.setOnPreparedListener(new OnPreparedListener(){

  @Override

  public void onPrepared(MediaPlayer mp) {

  // 這時能確保player處於Prepared狀態,觸發start是最合適的

  mp.start();

  }

  });

  mp.setOnCompletionListener(new OnCompletionListener() {

  @Override

  public void onCompletion(MediaPlayer mp) {

  // 正常播放結束,可以觸發播放下一首

  }

  });

  mp.setOnErrorListener(new OnErrorListener() {

  @Override

  public boolean onError(MediaPlayer mp, int what, intextra) {

  // 操作錯誤或其他原因導致的錯誤會在這裡被通知

  return true;

  }

  });

  // 連接並加載資源

  try {

  mp.prepare();

  // mp.prepareAsync() 這也是可以的,這是異步處理,上面的是同步處理,實際加載完畢以OnPreparedListener.onPrepared()為准。

  } catch (IllegalStateException e) {

  e.printStackTrace();

  } catch (IOException e) {

  e.printStackTrace();

  }

  // mp.start(); // 建議在OnPreparedListener.onPrepared()回調中觸發該函數,特別是使用異步加載時

  /**

  * ... 你的其他操作 ...

  */

  // 終止播放並釋放資源

  try{

  mp.stop(); // 這是必要的,如果你設置了循環播放,否則程序退出了音樂仍在後台繼續播...

  mp.release();

  }catch(IllegalStateException e){

  e.printStackTrace();

  }

  // 通過new創建後的player處於Idle狀態

  接上頁

  MediaPlayer mp = new MediaPlayer();

  if(mp==null){

  // new創建有可能會返回null值,檢測是好的習慣

  return;

  }

  // 設置資源路徑,成功執行的話player將處於Initialized狀態

  try {

  mp.setDataSource("/sdcard/test.mp3"); // 直接傳URL也是可以的,將自動處理緩沖

  } catch (IllegalArgumentException e) {

  e.printStackTrace();

  } catch (IllegalStateException e) {

  // 如果在非Idle狀態下調用setDataSource就會導致該異常

  e.printStackTrace();

  } catch (IOException e) {

  e.printStackTrace();

  }

  // 設置必要的監聽器

  mp.setOnPreparedListener(new OnPreparedListener(){

  @Override

  public void onPrepared(MediaPlayer mp) {

  // 這時能確保player處於Prepared狀態,觸發start是最合適的

  mp.start();

  }

  });

  mp.setOnCompletionListener(new OnCompletionListener() {

  @Override

  public void onCompletion(MediaPlayer mp) {

  // 正常播放結束,可以觸發播放下一首

  }

  });

  mp.setOnErrorListener(new OnErrorListener() {

  @Override

  public boolean onError(MediaPlayer mp, int what, intextra) {

  // 操作錯誤或其他原因導致的錯誤會在這裡被通知

  return true;

  }

  });

  // 連接並加載資源

  try {

  mp.prepare();

  // mp.prepareAsync() 這也是可以的,這是異步處理,上面的是同步處理,實際加載完畢以OnPreparedListener.onPrepared()為准。

  } catch (IllegalStateException e) {

  e.printStackTrace();

  } catch (IOException e) {

  e.printStackTrace();

  }

  // mp.start(); // 建議在OnPreparedListener.onPrepared()回調中觸發該函數,特別是使用異步加載時

  /**

  * ... 你的其他操作 ...

  */

  // 終止播放並釋放資源

  接上頁

  try{

  mp.stop(); // 這是必要的,如果你設置了循環播放,否則程序退出了音樂仍在後台繼續播...

  mp.release();

  }catch(IllegalStateException e){

  e.printStackTrace();

  }

  播放控制上基本與SoundPool相同有:

  start()、pause()、stop()、seekTo()、setLooping()...

  需要注意的是, 循環播放設置上與SoundPool不同,不能指定確定的循環次數,而是一個布爾值,指定是否循環播放...

  更多的函數使用請查閱API文檔。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved