進階音效控制與管理

来源:互联网 发布:淘宝内部购物券网站 编辑:程序博客网 时间:2024/05/23 19:14

摘要

以 XNA 為基礎的遊戲程式可以利用 SoundEffect 類別的功能執行簡單的音效播放,或是利用 SoundEffectInstance 類別進行進階的音效播放控制,包括播放、暫停、恢復、結束、是否要播放 3D 的音效、控制聲音從左邊或右邊的聲道輸出、控制音調高低、以及控制音量等等。在這一回的文章中,我們將介紹利用 SoundEffectInstance 類別進行進階音效控制的做法,除此之外,我們也將會介紹支援組織和管理音效檔案的 XACT(Cross-platform Audio Creation Tool) 工具。

有關 XNA Framework 提供的 SoundEffect 類別和 SoundEffectInstance 類別的基本功能和使用方法可以參考 [XNA 互動式遊戲設計] 一文的說明。

 進階音效控制

Windows Phone 7 智慧型手機並非只能播放一種音效,以 XNA 為基礎的遊戲程式可以同時播放 64 種聲音,達成混合多種音效的效果。除此之外,XNA Framework 也支援動態播放音效,例如播放應用程式執行時期建立的音效,或是搭配麥克風播放由外界輸入的音效。

[提示]

Windows Phone 7 智慧型手機支援同時播放多達 64 種的音效,但是 Xbox 360 遊戲機支援播放多達 300 種的音效,可以播放更豐富的聲音效果,而 Windows 作業系統則是未限制可以同時播放的音效數目。

當遊戲程式想要模擬環繞音效的時候,可以利用 SoundEffectInstance 類別的 Pan 屬性控制音效要從左方發聲,還是要從右方發聲。Pan 屬性是一個介於 -1.0 與 1.0 之間的數值,其值為 -1.0 表示要從左方發聲,其值為 1.0 表示要從右方發聲,而其值為 0 則表示要從中央發聲,其預設值為 0。

當遊戲程式需要控制所播放的音效的音調高低時,就可以利用 SoundEffectInstance 類別的 Pitch 屬性進行控制。Pitch 屬性是一個介於 -1.0 與 1.0 之間的數值,其值為 -1.0 表示音調最低,其值為 1.0 表示音調最高,其預設值為 0。

當遊戲程式需要控制所播放的音效的音量時,就可以利用 SoundEffectInstance 類別的 Volume 屬性進行控制。Volume 屬性是一個介於 .0 與 1.0 之間的數值,其值為 0 表示音量最小 (靜音),其值為 1 表示音量最大,其預設值為 1。請注意透過 SoundEffectInstance 類別控制的音量取決於 SoundEffect 類別的 MasterVolume 屬性的內容值,換句話說將 SoundEffectInstance 類別的 Volume 屬性的內容值設定為 1,所呈現的最大音量就是 SoundEffect 類別的 MasterVolume 屬性設定的音量。

例如遊戲程式想呈現環繞音效的效果,可以先把欲播放的音效檔案加入到 Content Pipeline 專案中,然後於 Game1 類別宣告以下的變數,負責管理音效資源、SoundEffectInstance 類別的物件、以及記錄音效移動的方向:

XML
SoundEffect RingOut;//管理音效資源的變數SoundEffectInstance RingOutEffect;//管理 SoundEffectInstance 類別的物件的變數bool bPaneLeft = false;//記錄音效移動方向的變數

宣告妥變數之後請於 Game1 類別的 LoadContent 方法加入以下的程式碼,負責載入音效資源和建立 SoundEffectInstance 類別的物件:

XML
RingOut = Content.Load<SoundEffect>("RingOut");//載入名稱為 RingOut 的音效資源RingOutEffect = RingOut.CreateInstance();//建立 SoundEffectInstance 類別的物件RingOutEffect.IsLooped = true;//設定音效要不斷地重覆播放

載入音效資源並建立妥 SoundEffectInstance 類別的物件之後,我們只要在 Game1 類別的 Update 方法中變更 SoundEffectInstance 類別的物件的 Pan 屬性的內容值,就可以變更音效發聲的位置,例如以下的程式:

XML
if (bPaneLeft)//判斷音效發聲的位置是否為往左移動{    if (RingOutEffect.Pan - 0.1 <= -1)//如果已經無法往左移動    {        RingOutEffect.Pan = -1f;//設定音效從左方發聲        bPaneLeft = true;//設定移動方向為往右移動    }    else        RingOutEffect.Pan -= 0.1f;//設定發聲位置往左移動 0,1}else//音效發聲的位置為往右移動{    if (RingOutEffect.Pan + 0.1 >= 1)//如果已經無法往右移動    {        RingOutEffect.Pan = 1f;//設定音效從右方發聲        bPaneLeft = false;//設定移動方向為往左移動    }    else        RingOutEffect.Pan += 0.1f;//設定發聲位置往右移動 0,1}

做好之後請執行程式,遊戲程式播放的音效就會從逐漸移至右方發聲,移至最右方之後再逐漸移至左方進行發聲,移至最左方之後再逐漸移至右方進行發聲,周而復始,模擬音效環繞的效果。

[提示]

以 XNA 為基礎的遊戲程式可以使用類似的技巧控制所播放的音效的音調和音量,模擬音量由大變小,由小變大,或是音調由高變低,或是由低變高的效果。

 動態音效支援

XNA Game Studio 4.0 在支援音效控制方面加入了一些新的控制功能,包括支援從 PCM 格式的音效資料流建立 SoundEffect 類別的物件,直接從 PCM 格式的音效資料流進行播放動態音效的功能,以及錄製從麥克風輸入的聲音的功能,詳細說明如下:

  • XNA Game Studio 4.0 支援的 SoundEffect 類別的建構函式允許接受 PCM 格式的資料流型態的參數,建立能夠從 PCM 格式的資料流播放音效的 SoundEffect 類別的物件。以下就是一個能夠利用第一個參數指定 PCM 格式的音效資料流用來建立 SoundEffect 類別的物件的建構函式:
XML
public SoundEffect (         byte[] buffer,         int sampleRate,         AudioChannels channels)

以下就是利用 TitleContainer 類別的 OpenStream 方法從副檔名為 .wav 格式的音效檔案讀入 PCM 格式的音效資料流,再據以建立 SoundEffect 類別的物件:

XML
SoundEffect soundFromFile = SoundEffect.FromStream(TitleContainer.OpenStream(@"Content\Sample.wav")); //利用 WAV 格式音效檔案的內容建立 SoundEffect 類別的物件soundFromFile.Play();//播放 SoundEffect 類別的物件管理的音效資料

使用上述的方式播放音效資料流,以 XNA 為基礎的遊戲程式仍然需要將欲播放的 WAV 格式的音效檔案預先加入到 Content Pipeline 專案,但是必須將 WAV 格式的音效檔案的 [Build Action] 屬性的內容值從預設的 Compile 改成 Content,再將 [Copy to Output Directory] 屬性的內容值改成:Copy always,表示不要將 WAV 格式的音效檔案建置成 XNB 格式的二進位資料,而要隨著遊戲程式一起部署到 Windows Phone 7 智慧型手機,供遊戲程式執行時期載入使用,並直接從 WAV 格式的音效檔案載入欲播放的音效。

[注意]

第一個參數限制必須為 PCM 格式、頻率為 8KHz 或 48KHz、單聲道或立體聲的音效資料。

  • 除了 SoundEffect 類別能夠支援播放格式為音效資料流的音效之外,XNA Game Studio 4.0 提供的 DynamicSoundEffectInstance 類別也支援遊戲程式於執行階段建立音效資料,供遊戲程式執行的時候播放。DynamicSoundEffectInstance 類別常用的屬性請參考表1 的說明:
表1:DynamicSoundEffectInstance 類別常用的屬性
屬性名稱說明IsLooped控制音效是否要不斷地重覆播放的屬性。Pan控制音效發聲位置的屬性。PendingBufferCount查詢放置播放音效資料的緩衝區數目。Pitch控制音調高低的屬性。State表示音效播放狀態的屬性,可能狀態為:playing (播放中)、paused (暫停)、stopped (停止)。Volume控制音量大小的屬性。

 

DynamicSoundEffectInstance 類別常用的方法請參考表2 的說明:

表2:DynamicSoundEffectInstance 類別常用的方法
方法名稱說明Apply3D播放 3D 效果的音效。GetSampleDuration依據存放音效資料的緩衝區的資料量計算音效長度。GetSampleSizeInBytes依據存放音效資料的緩衝區的時間長度計算資料量。Pause暫停播放音效。Play執行播放音效的動作。Resume恢復播放音效的動作。Stop停止播放音效。SubmitBuffer指定存放音效資料的緩衝區為欲播放的音效。

 

表3:DynamicSoundEffectInstance 類別常用的事件
方法名稱說明BufferNeeded當存放音效資料以供播放的緩衝區數目小於或等於 2 時引發的事件。

 

欲使用 DynamicSoundEffectInstance 類別執行動態音效播放,您可以於 Game1 類別宣告以下的變數:

XML
DynamicSoundEffectInstance dynamicSound;//管理 DynamicSoundEffectInstance 類別//的物件的變數int position;   //指定從第幾個位元組的音效資料開始播放int count;//指定欲播放的音效的位元組數byte[] byteArray;//存放音效資料的位元組陣列

然後於 Game1 類別的 LoadContent 方法加入以下的程式碼,從音效檔案讀入欲播放的音效資料:

XML
Stream waveFileStream = TitleContainer.OpenStream(@"Content\RingOut.wav"); //依據欲播放的音效檔案建立 Stream 類別的物件BinaryReader reader = new BinaryReader(waveFileStream);//為 Stream 類別的物件建立 BinaryReader 類別的物件int chunkID = reader.ReadInt32();//讀取 Chunk IDint fileSize = reader.ReadInt32();//讀取檔案大小int riffType = reader.ReadInt32();//讀取 riff 型態int fmtID = reader.ReadInt32();//讀取 fmt IDint fmtSize = reader.ReadInt32();//讀取 fmt 大小int fmtCode = reader.ReadInt16();//讀取 fmt codeint channels = reader.ReadInt16();    //讀取 channel 數目int sampleRate = reader.ReadInt32();//讀取 Sample Rateint fmtAvgBPS = reader.ReadInt32();   //讀取 fmt 的平均 BPSint fmtBlockAlign = reader.ReadInt16();//讀取 fmt Block Align 數據int bitDepth = reader.ReadInt16();//讀取位元深度if (fmtSize == 18)    //如果 fmt 大小為18{      int fmtExtraSize = reader.ReadInt16();  //讀取 fmt 額外的大小      reader.ReadBytes(fmtExtraSize);       //讀入等同於 fmt 額外大小的資料}int dataID = reader.ReadInt32();//讀取音效資料 IDint dataSize = reader.ReadInt32();   //讀取音效資料大小byteArray = reader.ReadBytes(dataSize);         //將音效資料大小的音效資料讀入到緩衝區dynamicSound = new DynamicSoundEffectInstance(sampleRate, (AudioChannels)channels);//依據 Sample Rate 和 Channel 建立 DynamicSoundEffectInstance 類別的物件count = dynamicSound.GetSampleSizeInBytes(TimeSpan.FromMilliseconds(100));//依據依據存放音效資料時間長度計算資料量dynamicSound.BufferNeeded += new EventHandler<EventArgs>(DynamicSound_BufferNeeded);//指定處理 BufferNeeded 事件的事件處理程序dynamicSound.Play();//播放 DynamicSoundEffectInstance 類別的物件管理的音效

接下來請為 Game1 類別加入以下的方法,負責處理 DynamicSoundEffectInstance 類別的物件的 BufferNeeded 事件:

XML
void DynamicSound_BufferNeeded(object sender, EventArgs e){    dynamicSound.SubmitBuffer(byteArray, position, count);//從 byteArray 陣列第 position 位元組開始,取出 count 個位//元組供 DynamicSoundEffectInstance 類別的物件進行播放    position += count;//遞增 position 變數的內容值    if (position + count > byteArray.Length)  //如果 byteArray 陣列的內容已全部播放完畢    {        position = 0;//將 position 變數的內容值歸零    }}

[注意]

使用上述的方式播放音效資料流,以 XNA 為基礎的遊戲程式仍然需要將欲播放的 WAV 格式的音效檔案預先加入到 Content Pipeline 專案,但是必須將 WAV 格式的音效檔案的 [Build Action] 屬性的內容值從預設的 Compile 改成 Content,再將 [Copy to Output Directory] 屬性的內容值改成:Copy always,表示不要將 WAV 格式的音效檔案建置成 XNB 格式的二進位資料,而要隨著遊戲程式一起部署到 Windows Phone 7 智慧型手機,供遊戲程式執行時期載入使用,並直接從 WAV 格式的音效檔案載入欲播放的音效。

做好之後請按下 Ctrl+F5 組合鍵執行遊戲程式,就可以聽到 DynamicSoundEffectInstance 類別的物件播放的動態音效。

  • XNA Game Studio (4.0) 提供的 Microphone 類別可以協助程式利用 Windows Phone 7 內建的麥克風錄製外界輸入的聲音。有關 Microphone 類別常用的屬性可以參考表4 的說明:
表4:Microphone 類別常用的屬性
屬性名稱說明All取得所有可用的麥克風。BufferDuration存放從麥克風輸入的音訊資料的時間長度。Default取得預設使用的麥克風。IsHeadset判斷是否為耳機式麥克風。SampleRate取得使用麥克風執行錄音時的 Sample Rate。State取得麥克風錄音的狀態。

 

Microphone 類別常用的方法可以參考表5 的說明:

表5:Microphone 類別常用的方法
方法名稱說明GetData從麥克風取得最新錄製的聲音資料。GetSampleDuration查詢緩衝區中待播放的音效資料的時間長度。GetSampleSizeInBytes查詢利用麥克風錄製指定時間長度的聲音需要的位元組數。Start開始錄製聲音的動作。Stop結束錄製聲音的動作。

 

Microphone 類別常用的事件請參考表6的說明:

表6:Microphone 類別常用的事件
方法名稱說明BufferReady當存放錄製聲音資料的緩衝區準備好可供播放時引發的事件。

 

接下來我們就要利用 Microphone 類別提供的類別,錄製透過 Windows Phone 7 內建的麥克風輸入的聲音,供程式進行播放。

首先請於 Game1 類別宣告以下的變數,負責管理 DynamicSoundEffectInstance 類別的物件:

XML
DynamicSoundEffectInstance sound;

然後於 Game1 類別的 Initialize 方法加入以下的程式碼,負責啟動錄音的動作:

XML
sound = new DynamicSoundEffectInstance(Microphone.Default.SampleRate, AudioChannels.Mono);//利用麥克風預設的 Sample Rate 錄製單聲道的聲音//處理錄製妥透過麥克風輸入的聲音引發的 BufferReady 事件Microphone.Default.BufferReady += (a, b) =>{    byte[] data = new byte[Microphone.Default.GetSampleSizeInBytes(Microphone.Default.BufferDuration)];//建立存放聲音資料的位元組陣列    Microphone.Default.GetData(data);//取得錄製的聲音,並儲存到 data 位元組陣列中    sound.SubmitBuffer(data);//將位元組陣列的內容交給 DynamicSoundEffectInstance 類//別的物件進行播放};Microphone.Default.Start();//啟動麥克風執行錄製聲音的工作

最後我們只要在 Game1 類別的 Update 方法中加入以下的程式碼,在錄製5秒鐘的聲音之後停止錄製的動作,並將錄製妥的聲音交由 DynamicSoundEffectInstance 類別的物件進行播放:

XML
if (gameTime.TotalGameTime.TotalSeconds > 5)//如果錄製的時間是否超過 5 秒{    Microphone.Default.Stop();//關閉麥克風錄音的功能    sound.Play();//將錄製妥的聲音交由 DynamicSoundEffectInstance 類別的物件進行播放}

做好之後請執行程式,然後對麥克風講話,5 秒鐘之後,程式就會播放剛剛錄製的聲音。

範例下載:AdvSoundEffect.zip

 認識 XACT (Cross-platform Audio Creation Tool) 聲音組識與管理工具

XNA Game Studio 除了提供類別庫供遊戲程式製作所需要的功能以外,也提供了一系列輔助遊戲開發的工具,包括:Microsoft Cross-Platform Audio Creation Tool 3 (XACT3)、XACT Auditioning Utility、和 XNA Framework Remote Performance Monitor,其中的 Microsoft Cross-Platform Audio Creation Tool 3 是一個支援聲音組識與管理的工具,可以將遊戲程式需要使用的音效事先組織妥當,並設定好必要的屬性,降低遊戲程式製作因管理音效的播放衍生的複雜性。

[注意]

Windows Phone 7 平台目前尚未支援使用 Microsoft Cross-Platform Audio Creation Tool 3 工具建立的音效檔案,所以使用 Microsoft Cross-Platform Audio Creation Tool 3 工具建立的音效檔案只能供在 Windows 平台和 Xbox 360 遊戲平台上執行的遊戲程式使用。

要使用 Microsoft Cross-Platform Audio Creation Tool 3 工具組識和管理遊戲程式執行時期欲播放的音效,請執行 [所有程式 | Microsoft Game Studio 4.0 | Tools | Microsoft Cross-Platform Audio Creation Tool 3 (XACT3)] 功能,啟動 XACT3 工具,然後執行 [File | New Project] 功能建立新專案。專案建立成功之後請使用滑鼠的右鍵點中名稱為 [Wave Banks] 的項目,從出現的功能表選擇 [New Wave Bank] 功能,執行建立 Wave Bank 的工作,然後從檔案總管將欲管理的 .WAV 格式的聲音檔案拖曳至新建立的 Wave Bank 中。做好之後請使用滑鼠的右鍵點中名稱為 [Sound Banks] 的項目,從出現的功能表選擇 [New Sound Bank],執行建立 Sound Bank 的工作,建立好 Sound Bank 之後,請從 Wave Bank 視窗將欲管理的音效檔案拖曳至新建立的 Sound Bank 視窗的下方視窗中,得到一個預設的 Cue Name,請注意 Cue Name 即為遊戲程式播放 XACT3 管理的音效的名稱。當我們從 Wave Bank 視窗將欲管理的音效檔案拖曳至新建立的 Sound Bank 視窗的下方視窗時,Sound Bank 視窗的上方視窗也會自動加入一個新項目,您可以點選這個項目,再利用 XACT3 工具左下角的視窗設定音效的屬性,例如您可以設定音效重覆播放的次數 (設定 Looping 屬性,勾選 Infinite 欄位表示要不斷地重覆播放),如圖1 所示:

圖1:使用 XACT3 工具管理遊戲程式欲使用的音效的畫面

設定完成後請儲存專案,然後將 XACT3 工具關閉,所儲存的專案是一個副檔名為 .xap 的檔案,我們只要將這個檔案加入到遊戲程式的 Content Pipeline 專案中,就可以供遊戲程式執行的時候載入播放。

要使用 Content Pipeline 專案中利用 XACT3 工具建立的音效檔案,遊戲程式可以在 Game1 類別中宣告下列的變數:

XML
AudioEngine audioEngine;//管理音效播放工作的變數WaveBank waveBank;//管理 Wave Bank 內容的變數SoundBank soundBank; //管理 Sound Bank 內容的變數

然後在 Game1 類別的 Initialize 方法中加入以下的程式碼,建立管理音效播放工作的物件、管理 Wave Bank 內容的物件、以及管理 Sound Bank 內容的物件:

XML
audioEngine = new AudioEngine("Content\\MyAudio.xgs");    //建立 AudioEngine 類別的物件waveBank = new WaveBank(audioEngine, "Content\\Wave Bank.xwb");      //建立 Wave Bank 類別的物件soundBank = new SoundBank(audioEngine, "Content\\Sound Bank.xsb"); //建立 Sound Bank 類別的物件

請注意建立 AudioEngine 類別物件時指定的是 XACT3 的專案名稱,其副檔名為 .xgs,建立 WaveBank 類別的物件時指定的是 Wave Bank 的名稱,其副檔名為 .xwb,而建立 SoundBank 類別的物件時指定的是 Sound Bank 的名稱,其副檔名為 .xsb。

建立好必要的物件之後,請於 Game1 類別的 Update 方法加入以下的程式碼,準備欲播放的音效內容,並呼叫 AudioEngine 類別物件的 PlayCue 方法執行播放音效的動作:

XML
audioEngine.Update();soundBank.PlayCue("RingOut");

請注意呼叫 AudioEngine 類別物件的 PlayCue 方法所傳入的是 Cue Name,意即要播放 Cue Name 所代表的音效檔案。做好之後只要執行程式就可以聽到程式播放 XACT3 工具管理的音效。

原创粉丝点击