×

DirectX与VB.NET编程(七)声音引擎

Kalet Kalet 发表于2009-03-20 12:00:14 浏览170 评论0

抢沙发发表评论

在掌握了DirectSound的一些基本知识后,需要开始进行研究性的学习了。
这次讲的是一个简单的DirectSound声音引擎。


===============华丽的分割线===============

DirectX与VB.NET编程(七)声音引擎

学习内容
·掌握使用引擎的好处;
·掌握声音引擎的基本原理;
·能够独立编写简单的引擎;
·编写具有更多扩展功能的引擎。


===============华丽的分割线===============


相信您应该可以熟练地控制声音的加载、播放、停止甚至更高级的功能了吧,虽然这些功能看起来很有用,很方便,但是需要注意的是,这种简单的播放仍然有很大的局限性,假设在一款游戏中,有两个人在走路,他们当然会发出脚步声,但是不知道您是否注意到没有,当一声脚步声正在播放的时候,很可能会有另外一个脚步声想起,如果您只定义了一个缓存区,那么您应该怎么办?停止当前的声音去播放新的声音?显然不行,不播放新的声音更加不行,这样就陷入了两难的境地。
要解决这个问题,有一种比较常用的方法,即为每个声音建立多个拷贝,当脚步一响起时,播放拷贝一的声音,脚步二响起,播放拷贝二,而此时拷贝一仍在播放,这就很好地解决了问题。
但是如此建立拷贝必定会消耗大量内存,,因此可以循环利用一下,当脚步一结束时,脚步二还在继续,但是此时需要响起脚步三,那么原先脚步一的拷贝就可以派上用场了,这样即解决了问题,有很好地利用了内存。
可能还会有人问,一个游戏如此多的声音全部加载进内存本身就是一个艰巨的工程,况且还要多个拷贝,内存开销不可想象,这个问题也很好解决,比如玩一个单机游戏,玩的是战士,那么法师方面的声音就没有必要加载了,我在地图一,那么只要加载地图一的声音,进入地图二后把地图一的声音释放,加载地图二的,这个问题便也迎刃而解。
可是新的问题又来了,如此处理声音,虽然播放顺畅,但是代码却麻烦得惊人,因此,在这里引入了声音引擎的概念,让声音引擎来管理这些不听话的声音当然再好不过了,只要使用一些统一的方法便可从容处理。


===============华丽的分割线===============


本例的引擎仅仅是一些比较简单的操作,希望可以抛砖引玉,让您写出更加优秀的引擎。
首先介绍声音引擎的工作原理:
本引擎可以装载、卸载、播放和停止声音,并提供一些简单的属性以供查询。
引擎中已经事先定义好了缓冲区及其附属变量,缓冲区是一个二维数组,维一用来区分不同的声音,维二用来储存声音的数个拷贝。
加载声音时查询数组的维一来寻找空闲的缓冲区,再根据需要的数量创建拷贝;
卸载声音时将指定声音的所有拷贝全部回收;
播放声音时在指定声音中寻找空闲拷贝,然后播放之;
停止声音时将指定的声音停止即可。


===============华丽的分割线===============


由于没有新的学习点,因此直接给出本人编写的引擎及简单应用的全部代码,直接复制进代码窗口中即可。代码中有详尽的注释,以帮助理解:
Imports Microsoft.DirectX.DirectSound
Imports Microsoft.DirectX


Public Class Form1
       Inherits System.Windows.Forms.Form


       Dim Sounds As SoundBank
#Region " Windows 窗体设计器生成的代码 "


       Public Sub New()
           MyBase.New()


           '该调用是 Windows 窗体设计器所必需的。
           InitializeComponent()


           '在 InitializeComponent() 调用之后添加任何初始化


       End Sub


       '窗体重写 dispose 以清理组件列表。
       Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
           If disposing Then
               If Not (components Is Nothing) Then
                   components.Dispose()
               End If
           End If
           MyBase.Dispose(disposing)
       End Sub


       'Windows 窗体设计器所必需的
       Private components As System.ComponentModel.IContainer


       '注意: 以下过程是 Windows 窗体设计器所必需的
       '可以使用 Windows 窗体设计器修改此过程。
       '不要使用代码编辑器修改它。
       Friend WithEvents Button1 As System.Windows.Forms.Button
       Friend WithEvents Button2 As System.Windows.Forms.Button
       Friend WithEvents Button3 As System.Windows.Forms.Button
       <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
           Me.Button1 = New System.Windows.Forms.Button
           Me.Button2 = New System.Windows.Forms.Button
           Me.Button3 = New System.Windows.Forms.Button
           Me.SuspendLayout()
           '
           'Button1
           '
           Me.Button1.Location = New System.Drawing.Point(8, 8)
           Me.Button1.Name = "Button1"
           Me.Button1.Size = New System.Drawing.Size(144, 40)
           Me.Button1.TabIndex = 0
           Me.Button1.Text = "Button1"
           '
           'Button2
           '
           Me.Button2.Location = New System.Drawing.Point(8, 56)
           Me.Button2.Name = "Button2"
           Me.Button2.Size = New System.Drawing.Size(144, 32)
           Me.Button2.TabIndex = 1
           Me.Button2.Text = "Button2"
           '
           'Button3
           '
           Me.Button3.Location = New System.Drawing.Point(8, 96)
           Me.Button3.Name = "Button3"
           Me.Button3.Size = New System.Drawing.Size(144, 32)
           Me.Button3.TabIndex = 2
           Me.Button3.Text = "Button3"
           '
           'Form1
           '
           Me.AutoScaleBaseSize = New System.Drawing.Size(6, 14)
           Me.ClientSize = New System.Drawing.Size(292, 266)
           Me.Controls.Add(Me.Button3)
           Me.Controls.Add(Me.Button2)
           Me.Controls.Add(Me.Button1)
           Me.Name = "Form1"
           Me.Text = "Form1"
           Me.ResumeLayout(False)


       End Sub


#End Region


       Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
           Sounds = New SoundBank(Me)
           Sounds.LoadSound("a", "D:\魔兽争霸2\DRIVERS\DIGTEST.wav", 5)
           Sounds.LoadSound("b", "D:\生化危机\生化危机3复仇女神\data_a\voice\s001a030.wav", 8)
       End Sub


       Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
           Sounds.PlaySound("a", BufferPlayFlags.Default, False)
       End Sub


       Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
           Sounds.PlaySound("b", BufferPlayFlags.Default, False)
       End Sub


       Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
           MsgBox(Sounds.Count)
           Sounds.DisposeSound("a")
           Sounds.DisposeSound("b")
           MsgBox(Sounds.Count)
       End Sub
End Class


Class SoundBank


       '定义设备
       Dim Dev As Device
       '定义缓存区
       Dim BankBuff(15, 7) As SecondaryBuffer
       '定义缓存名称
       Dim BankName(15) As String
       '定义缓存拷贝数目
       Dim BankNumberic(15) As Integer
       '定义已用缓存数量
       Dim BankCount As Integer


       '构造函数
       Sub New(ByVal BindControl As Control)
           '初始化设备
           Dev = New Device
           Dev.SetCooperativeLevel(BindControl, CooperativeLevel.Normal)
           '初始化变量
           For i As Integer = 0 To 15
               BankName(i) = ""
               BankNumberic(i) = 0
           Next
           BankCount = 0
       End Sub


#Region "加载升级及其枚举"


       '加载声音
       Public Function LoadSound(ByVal SoundName As String, ByVal SoundPath As String, ByVal Numberic As Integer) As LoadResult
           '检查参数
           If SoundName = "" Or Numberic < 1 Or Numberic > 8 Then
               '参数非法
               Return LoadResult.ArgMistake
           End If
           '查找同名
           For i As Integer = 0 To 15
               If BankName(i).ToUpper = SoundName.ToUpper Then
                   '存在同名
                   Return LoadResult.Exists
               End If
           Next
           '查找空位进行插入


           For i As Integer = 0 To 15
               If BankName(i) = "" Then
                   BankName(i) = SoundName
                   BankCount = BankCount + 1
                   BankNumberic(i) = Numberic
                   '动态开辟缓存区DirectX与VB.NET编程(七)声音引擎
                   For j As Integer = 0 To BankNumberic(i) - 1
                       Dim BuffDes As New BufferDescription
                       BuffDes.ControlEffects = True
                       BuffDes.ControlPan = True
                       BuffDes.ControlVolume = True
                       BankBuff(i, j) = New SecondaryBuffer(SoundPath, BuffDes, Dev)
                   Next
                   '返回成功
                   Return LoadResult.Success
               End If
           Next
           '未找到空位
           Return LoadResult.FullCopy
       End Function


       '加载声音结果枚举
       Public Enum LoadResult As Integer
           '成功
           Success = 0
           '缓存已满
           FullCopy = 1
           '错误的参数
           ArgMistake = 2
           '已存在同名缓存
           Exists = 3
       End Enum


#End Region


#Region "卸载声音及其枚举"


       '卸载声音
       '大家可以在任务管理器中观察卸载前与卸载后内存的变化,确实已经释放了内存。
       Public Function DisposeSound(ByVal SoundName As String) As DisposResult
           '检查参数
           If SoundName = "" Then
               '参数非法
               Return DisposResult.ArgMistake
           End If
           For i As Integer = 0 To 15
               If BankName(i).ToUpper = SoundName.ToUpper Then
                   '清空名称和个数
                   BankName(i) = ""
                   BankNumberic(i) = 0
                   BankCount = BankCount - 1
                   For j As Integer = 0 To 7
                       If Not BankBuff(i, j) Is Nothing Then
                           '卸载缓存区
                           BankBuff(i, j).Dispose()
                       Else
                           Exit For
                       End If
                   Next
                   '卸载成功
                   Return DisposResult.Success
               End If
           Next
           '不存在缓存
           Return DisposResult.NotExists
       End Function


       '卸载声音结果枚举
       Public Enum DisposResult As Integer
           '成功
           Success = 0
           '不存在缓存
           NotExists = 1
           '错误的参数
           ArgMistake = 2
       End Enum


#End Region


#Region "播放声音及其枚举"


       '播放声音
       Public Function PlaySound(ByVal SoundName As String, ByVal PlayingMode As BufferPlayFlags, ByVal Restart As Boolean) As PlayResult
           '检查参数
           If SoundName = "" Then
               Return PlayResult.ArgMistake
           End If
           For i As Integer = 0 To 15
               If BankName(i).ToUpper = SoundName.ToUpper Then
                   '寻找空闲拷贝
                   For j As Integer = 0 To BankNumberic(i) - 1
                       If BankBuff(i, j).Status.Playing = False Then
                           '播放空闲拷贝
                           If Restart Then
                               BankBuff(i, j).SetCurrentPosition(0)
                           End If
                           BankBuff(i, j).Play(0, PlayingMode)
                           Return PlayResult.Success
                       End If
                   Next
                   '任务已满
                   Return PlayResult.FullQuene
               End If
           Next
           '不存在缓存
           Return PlayResult.NotExists
       End Function


       Public Enum PlayResult As Integer
           '成功
           Success = 0
           '不存在缓存
           NotExists = 1
           '参数错误
           ArgMistake = 2
           '播放任务已满
           FullQuene = 3
       End Enum


#End Region


#Region "停止播放及其枚举"


       Public Function StopSound(ByVal SoundName As String) As StopResult
           '检查参数
           If SoundName = "" Then
               Return StopResult.ArgMistake
           End If
           '寻找指定缓存
           For i As Integer = 0 To 15
               If BankName(i).ToUpper = SoundName.ToUpper Then
                   '停止所有拷贝
                   For j As Integer = 0 To BankNumberic(i) - 1
                       BankBuff(i, j).Stop()
                   Next
                   '停止成功
                   Return StopResult.Success
               End If
           Next
           '不存在缓存
           Return StopResult.NotExist
       End Function


       Public Enum StopResult As Integer
           '成功
           Success = 0
           '不存在缓存
           NotExist = 1
           '参数错误
           ArgMistake = 2
       End Enum


#End Region


#Region "已用缓存数量"


       Public ReadOnly Property Count()
           Get
               Return BankCount
           End Get
       End Property


#End Region


#Region "指定缓存的拷贝数"


       Public ReadOnly Property BuffCopies(ByVal Index As Integer)
           Get
               '检测参数
               If Index >= 1 And Index <= 8 Then
                   Return BankNumberic(Index)
               Else
                   Return -1
               End If
           End Get
       End Property


#End Region


End Class


===============华丽的分割线===============


相信各位已经对声音引擎有一个初步的了解。

DirectX与VB.NET编程(七)声音引擎

下次将会是DirectSound的扩展学习了,您可以直接跳过扩展学习,不过本人建议您还是看看为好。



群贤毕至

访客