Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android游戲編程之音頻編程

Android游戲編程之音頻編程

編輯:關於Android編程

如果你有一台Android設備,就會注意到當你按下增大或降低音量按鈕時,你所控制的不同音量設置取決於你正在運行的應用程序。在通話中,你控制的是輸入語音流的音量;在視頻播放器中,你控制的是視頻音頻的音量;在主屏幕上,你控制的是鈴聲的音量。 Android為不同的目的提供不同音頻流。當我們在游戲中播放音頻時,可使用類來輸出音效和音樂到特定的音樂流。不過,在我們想播放音效或音樂之前,需要確定音量按鈕控制了正確的音頻流。為此,我們使用Context接口的另一個方法: context.setVolumeControlStream(AudioManager.STREAM_MUSIC); 一如既往,Context的實現仍然由我們的活動來負責。調用該方法之後,音量按鈕就控制了該音樂流,後面我們就可使用它來輸出我們的音效和音樂。在活動的生命周期內我們只需要調用該方法一次,最好是在Activity.onCreate()方法中調用它。 首先我們要分清音樂流和音效的不同。後者一般是存儲在內存中且其長度不會超過幾秒鐘。Android系統給我們提供了一個SoundPool類,使用它可以很容易實現音效播放。 我們可以很簡單地初始化一個新的SoundPool實例,如下所示: SoundPool soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); 第一個參數指定在同一時刻我們最多能播放多少個音效。這並不是說我們不能加載更多的音效文件,它只不過是限制可同時播放的音效個數。第二個參數指定了SoundPool使用什麼音頻流來輸出該音頻。我們在這裡選擇音樂流,同時也已經為它設置好音量控件。最後一個參數現在沒有使用,它應該為默認值0. 為了從一個音頻文件加載音效到堆內存中,我們可使用SoundPool.load()方法。所有的文件都存儲在assets/目錄下,因此我們需要重載SoundPool.load()方法。所有的文件都存儲在assets/目錄下,因此我們需要重載SoundPool.load()方法來獲得一個AssetFileDescriptor。我們怎麼獲得AssetFileDescriptor呢?使用AssetManager。這裡我們使用SoundPool從assets/目錄加載一個名為explosion.ogg的OGG文件: AssetFileDescriptor descriptor = assetManager.openFd("explosion.ogg"); int explosionId = soundPool.load(descriptor, 1); 通過AssetManager.openFd()方法可直接獲得AssetFileDescriptor,而通過SoundPool可很容易地加載音效,第二個參數用於指定該音效的優先級。這個參數目前未使用,為了以後的兼容應設置為1. SoundPool.load()方法將返回一個整型值,它將作為一個句柄用於加載的音效。當我們想播放音效時,只需要指定該句柄,SoundPool就知道該播放哪個音頻。 soundPool.play(explosionId, 1.0f, 1.0f, 0, 0, 1); 第一個參數是從SoundPool.load()方法接受句柄。接下來兩個參數分別用於指定左右通道的音量,其值應該從0(靜音)到1(最大) 接下來兩個參數我們很少使用,其中第一個參數是優先級,目前沒有使用,並且應該設置為0.而另一個參數用於指定音效循環播放的頻率,一般不建議循環播放音效,因此設置為0。最後一個參數是播放速率,將其設置為大於1時,音效播放的速度將會比其在錄制時快;而將它設置為小於1時,播放該音效就會比較慢。 當我們不再需要一個音效並希望釋放內存時,可使用SoundPool.unload()方法: soundPool.unload(explosionId); 我們只需要將從SoundPool.load()方法接收的音效句柄傳入即可,該方法會將音效從內存卸載。 當我們完成所有的音效輸出且不再需要SoundPool時,需要調用SoundPool.release()方法來釋放SoundPool所占用的所有資源。當然,在釋放之後,我們不能再使用SoundPool,而且SoundPool所加載的所有音效也會被釋放。 現在編寫一個簡單的測試活動,每當單擊屏幕時它就播放一個爆炸音效。代碼如下: [java]   package org.example.ch04_android_basics;      import java.io.IOException;      import android.app.Activity;   import android.content.res.AssetFileDescriptor;   import android.content.res.AssetManager;   import android.media.AudioManager;   import android.media.SoundPool;   import android.os.Bundle;   import android.view.MotionEvent;   import android.view.View;   import android.view.View.OnTouchListener;   import android.widget.TextView;      public class SoundPoolTest extends Activity implements OnTouchListener{       SoundPool soundPool;       int explosionId = -1;          @Override       protected void onCreate(Bundle savedInstanceState) {           // TODO Auto-generated method stub           super.onCreate(savedInstanceState);           TextView textView = new TextView(this);           textView.setOnTouchListener(this);           setContentView(textView);                      setVolumeControlStream(AudioManager.STREAM_MUSIC);           soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0);                      try{               AssetManager assetManager = getAssets();               AssetFileDescriptor descriptor = assetManager                       .openFd("explosion.ogg");               explosionId = soundPool.load(descriptor, 1);           }catch(IOException e){               textView.setText("Couldn't load sound effect from asset, " +                       e.getMessage());           }       }          @Override       public boolean onTouch(View v, MotionEvent event) {           // TODO Auto-generated method stub           if(event.getAction() == MotionEvent.ACTION_UP){               if(explosionId != -1){                   soundPool.play(explosionId, 1, 1, 0, 0, 1);               }           }           return true;       }          @Override       protected void onPause() {           // TODO Auto-generated method stub           super.onPause();           soundPool.release();       }      }     SoundPool在處理MP3文件或長的音頻文件時會有問題,長文件的定義超過5或6秒鐘。一般建議使用OGG音頻文件來代替MP3文件,並盡可能使用低的采樣率和持續時間,同時保持音效質量。   短小的音效很時候放在Android應用程序從操作系統分配到的堆內存中,而包含較長音樂文件的大音頻文件就很不適合了。為此,我們就需要將音樂以流的方式輸出到音頻硬件上,這就意味著每次我們只能讀入一小塊數據,該數據足於解碼成原生的PCM數據並輸出到音頻芯片上。 這聽起來挺嚇人的。不過幸運的是,我們有MediaPlayer類,它能處理的所有事情。初始化MediaPlayer類: MediaPlayer mediaPlayer = new MediaPlayer(); 接下來我們需要告訴MediaPlayer播放什麼文件,這同樣通過AssetFileDescriptor來實現: AssetFileDescriptor descriptor = assetManager.openFd("music.ogg"); mediaPlayer.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength()); 這裡稍微比SoundPool中復雜一點。MediaPlayer.setDataSource()方法並沒有直接獲取AssetFileDescriptor,而是使用一個FileDescriptor,通過AssetFileDescriptor().getFileDescriptor()方法獲取該描述符,除此之外我們還需要指定音頻文件的偏移量和長度。為什麼是偏移量?實際上資源是以單個文件形式存儲的,為了讓MediaPlayer獲得文件的起始地址,我們需要將asset文件夾中的文件的偏移量提供給它。 在播放該音樂文件之前,需要再調用一個方法為MediaPlayer的播放做准備: MediaPlayer.prepare(); 這將實際地打開文件,檢查它是否可以讀取並用MediaPlayer實例來進行播放。從這裡開始,我們就可以隨意地播放、暫停和停止音頻文件,也可以設置循環播放和改變音量。 啟動播放可通過調用下面的方法進行: mediaPlayer.start(); 注意,該方法必須在成功調用MediaPlayer.prepare()方法之後才能調用(你將注意到它是否會拋出一個運行時異常)。 開始播放後,我們可以通過調用pause()方法來暫停播放: MediaPlayer.pause(); 只有我們成功准備好MediaPlayer並已啟動播放,調用此方法才會生效。為了恢復一個暫停的MediaPlayer,可再次調用MediaPlayer.start()方法而無需任何准備。 通過調用下面的方法可停止播放: MediaPlayer.stop(); 注意,當我們想啟動一個停止的MediaPlayer時,需要先再次調用MediaPlayer.prepare()方法。 我們可通過下面方法設置循環播放: MediaPlayer.setLooping(true); 可通過下面的方法來調整音樂播放的音量: MediaPlayer.setVolume(1, 1); 這會重新設置左右聲道的音量,文檔中沒有指定這兩個參數的設定范圍。從多次嘗試的結果看,有效值應該是0-1。 最後,我們需要一個方法來檢查該播放是否完成,有兩種方式可實現這一點。對於第一種方式,可向MediaPlayer注冊一個OnCompletionListener,當播放完成時它就會被調用: mediaPlayer.setOnCompletionListener(listener); 如果我們想輪詢MediaPlayer的狀態,則可使用下面方法: boolean isPlaying = mediaPlayer.isPlaying(); 注意,如果MediaPlayer被設置成循環播放,前面兩個方法都無法指示該MediaPlayer已停止。 最後,如果MediaPlayer實例完成了所有的操作,就需要通過下面的方法來確保它所占用的資源得以釋放: mediaPlayer.release(); 在丟棄一個實例前,執行這個操作應是很好的實踐。 如果我們沒有將MediaPlayer設置成循環且播放已結束,就可以通過MediaPlayer.prepare()和MediaPlayer.start()方法重啟MediaPlayer。 大多數這些方法都是異步的,因此當調用MediaPlayer.stop()時,MediaPlayer.isPlaying()方法可能還需要一點時間才能返回。 現在編寫一個測試活動,用循環模式播放assets/目錄下的一個音頻文件。該音效將根據活動的生命周期實現暫停和恢復,當活動暫停時,音樂也要暫停;而活動恢復時,音樂也要從上次暫停的地方開始播放。 代碼如下: [java]  package org.example.ch04_android_basics;      import java.io.IOException;      import android.app.Activity;   import android.content.res.AssetFileDescriptor;   import android.content.res.AssetManager;   import android.media.AudioManager;   import android.media.MediaPlayer;   import android.os.Bundle;   import android.widget.TextView;      public class MediaPlayerTest extends Activity {       MediaPlayer mediaPlayer;          @Override       protected void onCreate(Bundle savedInstanceState) {           // TODO Auto-generated method stub           super.onCreate(savedInstanceState);           TextView textView = new TextView(this);           setContentView(textView);                      setVolumeControlStream(AudioManager.STREAM_MUSIC);           mediaPlayer = new MediaPlayer();           try{               AssetManager assetManager = getAssets();               AssetFileDescriptor descriptor = assetManager.openFd("music.ogg");               mediaPlayer.setDataSource(descriptor.getFileDescriptor(),                       descriptor.getStartOffset(), descriptor.getLength());               mediaPlayer.prepare();               mediaPlayer.setLooping(true);           }catch(IOException e){               textView.setText("Couldn't load music file, " + e.getMessage());               mediaPlayer = null;           }       }          @Override       protected void onPause() {           // TODO Auto-generated method stub           super.onPause();           if(mediaPlayer != null){               mediaPlayer.pause();               if(isFinishing()){                   mediaPlayer.stop();                   mediaPlayer.release();               }  www.2cto.com         }       }          @Override       protected void onResume() {           // TODO Auto-generated method stub           super.onResume();           if(mediaPlayer != null)               mediaPlayer.start();       }   }     在onResume()方法中,我們只需啟動MediaPlayer(如果已經成功創建它)。onResume()方法是一個處理該操作的完美地方,因為它在onCreate()方法和onPause()方法之後被調用。在第一種情況下,它將第一次啟動播放;在第二種情況下,它將簡單地恢復已暫停的MediaPlayer。 在onPause()方法中,我們暫停MediaPlayer。如果該活動被銷毀,我們還需要停止該MediaPlayer並釋放所有資源
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved