05 进阶音效控制与管理

来源:互联网 发布:三菱q系列plc编程实例 编辑:程序博客网 时间:2024/06/05 01:53

摘要

以 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 类别的对象、以及记录音效移动的方向:

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

宣告妥变量之后请于 Game1 类别的 LoadContent 方法加入以下的程序代码,负责加载音效资源和建立 SoundEffectInstance 类别的对象:

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

加载音效资源并建立妥 SoundEffectInstance 类别的对象之后,我们只要在 Game1 类别的 Update 方法中变更 SoundEffectInstance 类别的对象的 Pan 属性的内容值,就可以变更音效发声的位置,例如以下的程序:

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 类别的对象的建构函式:
public SoundEffect (         byte[] buffer,         int sampleRate,         AudioChannels channels)

以下就是利用 TitleContainer 类别的 OpenStream 方法从扩展名为 .wav 格式的声音文件读入 PCM 格式的音效数据流,再据以建立 SoundEffect 类别的对象:

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 类别宣告以下的变量:

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

然后于 Game1 类别的 LoadContent 方法加入以下的程序代码,从声音文件读入欲播放的音效数据:

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 事件:

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 类别的对象:

DynamicSoundEffectInstance sound;

然后于 Game1 类别的 Initialize 方法加入以下的程序代码,负责启动录音的动作:

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 类别的对象进行播放:
if (gameTime.TotalGameTime.TotalSeconds > 5)//如果錄製的時間是否超過 5 秒{    Microphone.Default.Stop();//關閉麥克風錄音的功能    sound.Play();//將錄製妥的聲音交由 DynamicSoundEffectInstance 類別的物件進行播放}

做好之后请执行程序,然后对麦克风讲话,5 秒钟之后,程序就会播放刚刚录制的声音。

范例下载:AdvSoundEffect.zip


原创粉丝点击