Windows Phone 播放聲音的方法
在以Silverlight為主的Windows Phone 7應(yīng)用程序中播放音效,其實(shí)只有幾種方法,若要播放音效檔,比較常見(jiàn)的會(huì)用XNA Framework里的SoundEffect或SoundEffectInstance類別來(lái)播 放,在者就是用MediaElement來(lái)播放較長(zhǎng)的音樂(lè)或影片,本篇文章的主軸會(huì)以播放音效為主。使用不同的方法來(lái)播放音效有許多不同的注意事項(xiàng),一不小心應(yīng)用程序就會(huì)發(fā)生非預(yù)期例外,在此整理與分享一些使用上的心得筆記。
由于這兩個(gè)類別被編譯在Microsoft.Xna.Framework.dll組件中,因此使用前必須先從專案加入組件參考:
使用時(shí)要記得引用Microsoft.Xna.Framework與Microsoft.Xna.Framework.Audio命名空間:
由于開(kāi)發(fā)以Silverlight為基礎(chǔ)的應(yīng)用程序中使用SoundEffect或SoundEffectInstance類別來(lái)播放音效僅支援WAV格式 ( *.wav )的聲音檔,所以如果你想播放MP3格式的音效檔的話,必須先透過(guò)其他轉(zhuǎn)檔工具先轉(zhuǎn)換成WAV 格式才行,而且WAV 檔的格式還有以下 限制:
必須是PCM wave聲音檔,而且必須為RIFF bitstream格式
只能是mono (單音) 或stereo (立體音) 格式
必須為8-bits 或16-bits 的聲音檔
取樣頻率(Sample rate) 必須介于8,000 Hz 到48,000 Hz 之間
備注 :若要播放非WAV的聲音檔或音樂(lè)(例如MP3格式的音樂(lè)),可改用 MediaElement 來(lái)播放。
接著,確定一下載入到專案里的聲音檔的Build Action設(shè)定為Content
接著就能開(kāi)始寫(xiě)Code 了!在此,我們用簡(jiǎn)單的代碼來(lái)介紹其使用方法:
1.播放短時(shí)間的音效,適合使用SoundEffect類別來(lái)播放
- Stream stream = TitleContainer.OpenStream("Resources/NightAmbienceSimple_01.wav");
- SoundEffect effect = SoundEffect.FromStream(stream);
- FrameworkDispatcher.Update();
- effect.Play();
如上4行程序碼應(yīng)該很容易理解,第1 行載入聲音檔,第2 行建立SoundEffect 物件,第3 行最重要容后再述,第4 行播放聲音。
其中最重要的是第3行的FrameworkDispatcher . Update();語(yǔ)法,在使用SoundEffect播放聲音前,一定要先執(zhí)行這一段語(yǔ)法,否則就會(huì)引發(fā)例外!
然而,使用SoundEffect類別來(lái)播放有個(gè)最明顯的限制 ,那就是開(kāi)始播放音效后就無(wú)法透過(guò)程序來(lái)設(shè)定停止播放 ,因此直接使用SoundEffect來(lái)播放聲音通常會(huì)用來(lái)播放比較短的音效(例如1 ~ 3秒)。
2.播放連續(xù)的背景音效或較長(zhǎng)時(shí)間的音效,適合使用SoundEffectInstance類別來(lái)播放
- Stream stream = TitleContainer.OpenStream("Resources/NightAmbienceSimple_01.wav");
- SoundEffect effect = SoundEffect.FromStream(stream);
- SoundEffectInstance instance = effect.CreateInstance();
- instance.Play();
使用SoundEffectInstance來(lái)播放音效前,基本上還是要先取得SoundEffect物件,然后再透過(guò)SoundEffect的 CreateInstance來(lái)取得一個(gè)新的SoundEffectInstance實(shí)體, 不需要先執(zhí) 行 FrameworkDispatcher . Update(); 就可以播放聲音 ,而且也能透過(guò)其他方法來(lái)暫停、繼續(xù)或停止播放音效 ,這是與直 接使用SoundEffect物件來(lái)播放聲音時(shí)最大的差異。
另一個(gè)與SoundEffect播放音效的差異在于,使用SoundEffectInstance可以設(shè)定「重復(fù)播放音效」功能,這樣的功能可以運(yùn)用在需要 提供背景環(huán)境音效時(shí)使用。 啟用的方法也很簡(jiǎn)單,只要在SoundEffectInstance物件上設(shè)定IsLooped屬性為true即可。
§特別注意§
無(wú)論使用SoundEffect或SoundEffectInstance來(lái)播放音效,這兩種播放音效的方式有3個(gè)非常重要的注意事項(xiàng),分別說(shuō)明如下:
1. 這兩個(gè)類別來(lái)自于XNA Framework,因此只要有用到SoundEffect或SoundEffectInstance的頁(yè)面,都要在建構(gòu)子函式 中加上以下程序碼,并且不斷呼叫FrameworkDispatcher . Update();語(yǔ)法,這樣才能讓SoundEffect或 SoundEffectInstance在播放音樂(lè)的過(guò)程中正確無(wú)誤的執(zhí)行,否則很有可能會(huì)遇到音效播放到一半就自動(dòng)停止的情況! ( 更嚴(yán)重者,可能會(huì) 導(dǎo)致應(yīng)用程序直接發(fā)生非預(yù)期的例外 )
- // Timer to simulate the XNA game loop
- // (SoundEffect classes are from the XNA Framework)
- DispatcherTimer XnaDispatchTimer = new DispatcherTimer();
- XnaDispatchTimer.Interval = TimeSpan.FromMilliseconds(50);
- // Call FrameworkDispatcher.Update to update the XNA Framework internals.
- XnaDispatchTimer.Tick +=
- delegate { try { FrameworkDispatcher.Update(); } catch { } };
- // Start the DispatchTimer running.
- XnaDispatchTimer.Start();
2. 這兩個(gè)SoundEffect與SoundEffectInstance類別在播放音樂(lè)時(shí),其執(zhí)行的方式完全是屬于射后不理 (Fire-and- Forget)型的,所以當(dāng)啟動(dòng)聲音播放后會(huì)自動(dòng)在背景運(yùn)作(透過(guò)非同步執(zhí)行的方式),因此當(dāng)你希望循序播放兩個(gè)聲音檔時(shí),必須小心設(shè)計(jì),絕不能連續(xù)寫(xiě)在 一起,這樣會(huì)導(dǎo)致兩個(gè)音效檔同時(shí)播放。 你可以透過(guò)DispatcherTimer以非同步的方式來(lái)判斷前一個(gè)聲音檔是否已播放完畢來(lái)做到循序播放的目 的,以下是一個(gè)簡(jiǎn)易的程序碼范例供參考: ( 注: SoundEffect 與 SoundEffectInstance 都沒(méi)有事件可用 )
- bool isSound1_Played = false;
- bool isSound2_Played = false;
- void dt_Tick(object sender, EventArgs e)
- {
- if (false == isSound1_Played)
- {
- SoundEffect SoundSmall = SoundEffect.FromStream(TitleContainer.OpenStream("Audio/a1.wav"));
- soundInstance = SoundSmall.CreateInstance();
- soundInstance.IsLooped = false;
- soundInstance.Play();
- isSound1_Played = true;
- }
- else if (false == isSound2_Played && soundInstance.State == SoundState.Stopped)
- {
- SoundEffect sound = SoundEffect.FromStream(TitleContainer.OpenStream("Audio/a2.wav"));
- soundInstance = sound.CreateInstance();
- soundInstance.IsLooped = true;
- soundInstance.Play();
- isSound2_Played = true;
- }
- }
3. 也由于SoundEffectInstance類別射后不理 (Fire-and-Forget)的特性,所以請(qǐng)務(wù)必確認(rèn)在從頁(yè)面離開(kāi)時(shí),必須將 DispatcherTimer與SoundEffectInstance物件都設(shè)定停止,否則音樂(lè)即便到了下一頁(yè)還是會(huì)繼續(xù)播放的!
- protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
- {
- base.OnNavigatedFrom(e);
- dt.Stop(); // Stop the DispatcherTimer
- soundInstance.Stop();
- }
最后,我推薦各位可以下載AppHub上官方提供的Silverlight Sound Sample | Windows Phone范例,里面有些程序 碼可以參考使用,以下節(jié)錄LoadSound與LoadSoundInstance這兩個(gè)好用的Method給各位參考:
- /// <summary>
- /// Loads a wav file into an XNA Framework SoundEffect.
- /// </summary>
- /// <param name="SoundFilePath">Relative path to the wav file.</param>
- /// <param name="Sound">The SoundEffect to load the audio into.</param>
- private void LoadSound(String SoundFilePath, out SoundEffect Sound)
- {
- // For error checking, assume we'll fail to load the file.
- Sound = null;
- try
- {
- // Holds informations about a file stream.
- StreamResourceInfo SoundFileInfo = App.GetResourceStream(new Uri(SoundFilePath, UriKind.Relative));
- // Create the SoundEffect from the Stream
- Sound = SoundEffect.FromStream(SoundFileInfo.Stream);
- }
- catch (NullReferenceException)
- {
- // Display an error message
- MessageBox.Show("Couldn't load sound " + SoundFilePath);
- }
- }
- /// <summary>
- /// Loads a wav file into an XNA Framework SoundEffect.
- /// Then, creates a SoundEffectInstance from the SoundEffect.
- /// </summary>
- /// <param name="SoundFilePath">Relative path to the wav file.</param>
- /// <param name="Sound">The SoundEffect to load the audio into.</param>
- /// <param name="SoundInstance">The SoundEffectInstance to create from Sound.</param>
- private void LoadSoundInstance(String SoundFilePath, out SoundEffect Sound, out SoundEffectInstance SoundInstance)
- {
- // For error checking, assume we'll fail to load the file.
- Sound = null;
- SoundInstance = null;
- try
- {
- LoadSound(SoundFilePath, out Sound);
- SoundInstance = Sound.CreateInstance();
- }
- catch (NullReferenceException)
- {
- // Display an error message
- MessageBox.Show("Couldn't load sound instance " + SoundFilePath);
- }
- }