这次的例子是一个3D播放器,开始播放声音后不仅可以设置声源和听众的位置,还可以设置各种其它参数。
===============华丽的分割线===============
学习要点:
·掌握使用WaveFormat类;
·掌握使用Guid3DAlgorithm类;
·掌握使用主缓冲区和二级缓冲区;
·将DS3D的各项理论实践化。
===============华丽的分割线===============
首先,在讲解DS3D程序时把DS3D程序的大概流程列举出来:
1、初始化设备并绑定到窗口;
2、初始化WaveFormat(声音格式);
3、初始化主缓冲区并让其格式为实现定义的声音格式;
4、使用主缓冲区描述来初始化主缓冲区;
5、使用主缓冲区来初始化听众;
6、设定听众的属性;
7、初始化二级缓冲区描述,设定描述的Guid3DAlgorithm(声音质量)类并使用它来初始化二级缓冲区;
8、使用二级缓冲区来初始化3D音效缓冲区(声源);
9、设置3D音效缓冲区的属性;
10、播放声音;
11、调整各项参数以达到不同的播放效果。
这样说可能比较复杂,说形象点吧:
DS3D中有两个重要物体:听众和声源,声源可以有多个,但是听众却只能有一个,DS3D播放声音的原理是一个主缓冲区和多个二级缓冲区,每个二级缓冲区可以代表一个物体,它会发出自己的声音,这些声音当然就被加载在其对应的二级缓冲区里面了,而主缓冲区则只能有一个,代表听众,二级缓冲播放声音时就将它的声音灌输到主缓冲区里面,就好比各个声源的声音传播到听众耳朵里一样,这样就解释了主缓冲区和二级缓冲区的工作原理。基于这一点,二级缓冲区就必须与声源(3D音效缓冲区)对应,主缓冲区就必须与听众对应。本人在这里也是理解了很久,最终根据SDK中的C#例子看明白了(本人的SDK中不知为何找不到VB.NET的例子。。)
原理都明白了,就开始写代码了。
===============华丽的分割线===============
首先在窗口上面放置一个按钮(Button)、六个标签(Label)、六个文本框(TextBox)和一个图片框(PictureBox)。具体布局可以参考图1或者代码。
===============华丽的分割线===============
程序运行时加载声音并播放。
首先声明类全局变量:
Dim Dev As Device '设备
Dim Buff As Buffer '主缓冲区
Dim SBuff As SecondaryBuffer '二级缓冲区
Dim BDes As New BufferDescription '主缓冲区描述
Dim SBDes As New BufferDescription '二级缓冲区描述
Dim B3D As Buffer3D '3D缓冲区
Dim B3DS As New Buffer3DSettings '3D缓冲区描述
Dim FMT As WaveFormat '声音格式
Dim Lis As Listener3D '听众
Dim LisS As Listener3DSettings '听众属性
===============华丽的分割线===============
初始化设备和声音格式:
Dev = New Device
Dev.SetCooperativeLevel(Me, CooperativeLevel.Normal)
FMT = New WaveFormat
FMT.FormatTag = WaveFormatTag.Pcm
FMT.Channels = 2
FMT.SamplesPerSecond = 22050
FMT.BitsPerSample = 16
FMT.BlockAlign = CShort(FMT.BitsPerSample / 8 * FMT.Channels)
FMT.AverageBytesPerSecond = FMT.SamplesPerSecond * FMT.BlockAlign
其中声音格式的各项参数属性:
FormatTag:音频格式。
Channels:声道数,2代表立体声。
SamplesPerSecond:音频采样级别,每秒的采样数量。
BitsPerSample:音频采样大小,每个采样的大小。
BlockAlign:每位输出率,即采样输出的比特率(根据公式猜测)。
AverageBytesPerSecond:平均每秒字节数,该数乘以把就是平均每秒比特数,即Kbps。
其实这些属性大概就是控制声音的输出品质和速率吧,本人也没有尝试过。
===============华丽的分割线===============
下面设置主缓冲区描述,并使用该描述初始化主缓冲区:
BDes = New BufferDescription '初始化描述
BDes.PrimaryBuffer = True '设置为主缓冲区
BDes.Control3D = True '设置为3D可调整
BDes.Format = FMT '设置声音格式
Buff = New Buffer(BDes, Dev) '使用描述初始化主缓冲区
说明一下,PrimaryBuffer代表设置缓冲区为主缓冲区,Control3D与ControlPan和ControlVolume的效果是相同的,即设置缓冲区是否可调整3D音效,Format即让其声音格式等于刚才设置的格式。
===============华丽的分割线===============
下面开始是初始化听众:
Lis = New Listener3D(Buff)
LisS = Lis.AllParameters
Dim LPos As Vector3
Dim LVel As Vector3
LPos.X = 0
LPos.Y = 0
LPos.Z = 0
LVel.X = 0
LVel.Y = 0
LVel.Z = 0
LisS.Position = LPos
LisS.Velocity = LVel
Lis.AllParameters = LisS
使用主缓冲区初始化听众,然后使用Lis代替修改其位置与速度,本例中听众位于中央且不动,故如此设置。需要注意的是听众的AllParamenters必须这样设置,Lis.AllParamenters.X=0等诸如此类的赋值会引起编译器报错。其实上面也很多地方也涉及到这种赋值方法。
到此为止听众已经初始化完毕了,其实前面设置主缓冲区、主缓冲区描述和声音格式等均是为了听众作铺垫。
===============华丽的分割线===============
开始设置声源(3D缓冲区),首先需要设置一个二级缓冲区及其描述:
SBDes.Guid3DAlgorithm = DSoundHelper.Guid3DAlgorithmHrtfLight '设置品质为高
SBDes.Control3D = True '设置为3D可调整
SBuff = New SecondaryBuffer("D:\魔兽争霸2\DRIVERS\DIGTEST.WAV", SBDes, Dev) '使用描述初始化二级缓冲区
然后使用该二级缓冲区初始化3D缓冲区(声源):
B3D = New Buffer3D(SBuff) '使用二级缓冲区初始化3D缓冲区
B3DS = B3D.AllParameters
B3DS.Mode = Mode3D.HeadRelative
B3D.AllParameters = B3DS
这里设置3D缓冲区的Mode属性时也使用了与上面相同的方法来设置。
===============华丽的分割线===============
最后初始化文本框并播放声音:
TextBox1.Text = B3D.MaxDistance.ToString
TextBox2.Text = B3D.MinDistance.ToString
TextBox3.Text = Lis.RolloffFactor.ToString
TextBox4.Text = Lis.DopplerFactor.ToString
TextBox5.Text = B3D.ConeAngles.Inside.ToString
TextBox6.Text = B3D.ConeAngles.Outside.ToString
SBuff.Play(0, BufferPlayFlags.Looping) '声源开始发声
到此为止已经初始化3D声音并开始播放了。
===============华丽的分割线===============
用户应该可以调整声源的位置,以达到不同的播放效果,这里使用图片框进行调整,用户点击图片框时来调整声源的平面位置,因为这里还没有学习Direct3D因此忽略Y轴(声源与听众处于同一Y轴坐标0),同时由于没有学习DirectDraw,因此没有在所处位置绘出图形,您可以自行使用GDI绘制。修改位置的代码如下:
Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
Dim Pos As Vector3
Dim Vel As Vector3
Pos.X = (e.X - PictureBox1.Width / 2) / 200
Pos.Y = 0
Pos.Z = (e.Y - PictureBox1.Height / 2) / 200
Vel.X = 0
Vel.Y = 0
Vel.Z = 0
B3D.Position = Pos '设置声源的位置
B3D.Velocity = Vel '设置声源的速度
End Sub
===============华丽的分割线===============
如果用户需要修改参数,可以在相应文本框中输入值并点击“应用设置”即可:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim TS As Buffer3DSettings '声源属性
Dim LS As Listener3DSettings '
TS = B3D.AllParameters
LS = Lis.AllParameters
TS.MaxDistance = Val(TextBox1.Text) '设置最大距离
TS.MinDistance = Val(TextBox2.Text) '设置最小距离
LS.RolloffFactor = Val(TextBox3.Text) '设置衰减因子
LS.DopplerFactor = Val(TextBox4.Text) '设置多普勒因子
TS.InsideConeAngle = Val(TextBox5.Text) '设置内锥角
TS.OutsideConeAngle = Val(TextBox6.Text) '设置外锥角
B3D.AllParameters = TS
Lis.AllParameters = LS
End Sub
===============华丽的分割线===============
下面是程序的全部代码:
Imports Microsoft.DirectX.DirectSound
Imports Microsoft.DirectX
Public Class Form1
Inherits System.Windows.Forms.Form
Dim Dev As Device '设备
Dim Buff As Buffer '主缓冲区
Dim SBuff As SecondaryBuffer '二级缓冲区
Dim BDes As New BufferDescription '主缓冲区描述
Dim SBDes As New BufferDescription '二级缓冲区描述
Dim B3D As Buffer3D '3D缓冲区
Dim B3DS As New Buffer3DSettings '3D缓冲区描述
Dim FMT As WaveFormat '声音格式
Dim Lis As Listener3D '听众
Dim LisS As Listener3DSettings '听众属性
#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 PictureBox1 As System.Windows.Forms.PictureBox
Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
Friend WithEvents Label2 As System.Windows.Forms.Label
Friend WithEvents TextBox2 As System.Windows.Forms.TextBox
Friend WithEvents Label3 As System.Windows.Forms.Label
Friend WithEvents TextBox3 As System.Windows.Forms.TextBox
Friend WithEvents Label4 As System.Windows.Forms.Label
Friend WithEvents TextBox4 As System.Windows.Forms.TextBox
Friend WithEvents Button1 As System.Windows.Forms.Button
Friend WithEvents Label5 As System.Windows.Forms.Label
Friend WithEvents TextBox5 As System.Windows.Forms.TextBox
Friend WithEvents Label6 As System.Windows.Forms.Label
Friend WithEvents TextBox6 As System.Windows.Forms.TextBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.PictureBox1 = New System.Windows.Forms.PictureBox
Me.Label1 = New System.Windows.Forms.Label
Me.TextBox1 = New System.Windows.Forms.TextBox
Me.Label2 = New System.Windows.Forms.Label
Me.TextBox2 = New System.Windows.Forms.TextBox
Me.Label3 = New System.Windows.Forms.Label
Me.TextBox3 = New System.Windows.Forms.TextBox
Me.Label4 = New System.Windows.Forms.Label
Me.TextBox4 = New System.Windows.Forms.TextBox
Me.Button1 = New System.Windows.Forms.Button
Me.Label5 = New System.Windows.Forms.Label
Me.TextBox5 = New System.Windows.Forms.TextBox
Me.Label6 = New System.Windows.Forms.Label
Me.TextBox6 = New System.Windows.Forms.TextBox
Me.SuspendLayout()
'
'PictureBox1
'
Me.PictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
Me.PictureBox1.Location = New System.Drawing.Point(8, 8)
Me.PictureBox1.Name = "PictureBox1"
Me.PictureBox1.Size = New System.Drawing.Size(320, 320)
Me.PictureBox1.TabIndex = 0
Me.PictureBox1.TabStop = False
'
'Label1
'
Me.Label1.Location = New System.Drawing.Point(336, 8)
Me.Label1.Name = "Label1"
Me.Label1.Size = New System.Drawing.Size(56, 16)
Me.Label1.TabIndex = 1
Me.Label1.Text = "最大距离"
'
'TextBox1
'
Me.TextBox1.Location = New System.Drawing.Point(336, 24)
Me.TextBox1.Name = "TextBox1"
Me.TextBox1.Size = New System.Drawing.Size(144, 21)
Me.TextBox1.TabIndex = 2
Me.TextBox1.Text = ""
'
'Label2
'
Me.Label2.Location = New System.Drawing.Point(336, 56)
Me.Label2.Name = "Label2"
Me.Label2.Size = New System.Drawing.Size(64, 16)
Me.Label2.TabIndex = 3
Me.Label2.Text = "最小距离"
'
'TextBox2
'
Me.TextBox2.Location = New System.Drawing.Point(336, 72)
Me.TextBox2.Name = "TextBox2"
Me.TextBox2.Size = New System.Drawing.Size(144, 21)
Me.TextBox2.TabIndex = 4
Me.TextBox2.Text = ""
'
'Label3
'
Me.Label3.Location = New System.Drawing.Point(336, 104)
Me.Label3.Name = "Label3"
Me.Label3.Size = New System.Drawing.Size(56, 16)
Me.Label3.TabIndex = 5
Me.Label3.Text = "衰减因子"
'
'TextBox3
'
Me.TextBox3.Location = New System.Drawing.Point(336, 120)
Me.TextBox3.Name = "TextBox3"
Me.TextBox3.Size = New System.Drawing.Size(144, 21)
Me.TextBox3.TabIndex = 6
Me.TextBox3.Text = ""
'
'Label4
'
Me.Label4.Location = New System.Drawing.Point(336, 152)
Me.Label4.Name = "Label4"
Me.Label4.Size = New System.Drawing.Size(72, 16)
Me.Label4.TabIndex = 7
Me.Label4.Text = "多普勒因子"
'
'TextBox4
'
Me.TextBox4.Location = New System.Drawing.Point(336, 168)
Me.TextBox4.Name = "TextBox4"
Me.TextBox4.Size = New System.Drawing.Size(144, 21)
Me.TextBox4.TabIndex = 8
Me.TextBox4.Text = ""
'
'Button1
'
Me.Button1.Location = New System.Drawing.Point(400, 296)
Me.Button1.Name = "Button1"
Me.Button1.Size = New System.Drawing.Size(80, 24)
Me.Button1.TabIndex = 13
Me.Button1.Text = "应用设置"
'
'Label5
'
Me.Label5.Location = New System.Drawing.Point(336, 200)
Me.Label5.Name = "Label5"
Me.Label5.Size = New System.Drawing.Size(72, 16)
Me.Label5.TabIndex = 14
Me.Label5.Text = "内锥角"
'
'TextBox5
'
Me.TextBox5.Location = New System.Drawing.Point(336, 216)
Me.TextBox5.Name = "TextBox5"
Me.TextBox5.Size = New System.Drawing.Size(144, 21)
Me.TextBox5.TabIndex = 15
Me.TextBox5.Text = ""
'
'Label6
'
Me.Label6.Location = New System.Drawing.Point(336, 248)
Me.Label6.Name = "Label6"
Me.Label6.Size = New System.Drawing.Size(64, 16)
Me.Label6.TabIndex = 16
Me.Label6.Text = "外锥角"
'
'TextBox6
'
Me.TextBox6.Location = New System.Drawing.Point(336, 264)
Me.TextBox6.Name = "TextBox6"
Me.TextBox6.Size = New System.Drawing.Size(144, 21)
Me.TextBox6.TabIndex = 17
Me.TextBox6.Text = ""
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(6, 14)
Me.ClientSize = New System.Drawing.Size(488, 334)
Me.Controls.Add(Me.TextBox6)
Me.Controls.Add(Me.Label6)
Me.Controls.Add(Me.TextBox5)
Me.Controls.Add(Me.Label5)
Me.Controls.Add(Me.Button1)
Me.Controls.Add(Me.TextBox4)
Me.Controls.Add(Me.Label4)
Me.Controls.Add(Me.TextBox3)
Me.Controls.Add(Me.Label3)
Me.Controls.Add(Me.TextBox2)
Me.Controls.Add(Me.Label2)
Me.Controls.Add(Me.TextBox1)
Me.Controls.Add(Me.Label1)
Me.Controls.Add(Me.PictureBox1)
Me.MaximizeBox = False
Me.Name = "Form1"
Me.Text = "DS3D演示"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'初始化缓冲区
Dev = New Device
Dev.SetCooperativeLevel(Me, CooperativeLevel.Normal)
FMT = New WaveFormat
FMT.FormatTag = WaveFormatTag.Pcm
FMT.Channels = 2
FMT.SamplesPerSecond = 22050
FMT.BitsPerSample = 16
FMT.BlockAlign = CShort(FMT.BitsPerSample / 8 * FMT.Channels)
FMT.AverageBytesPerSecond = FMT.SamplesPerSecond * FMT.BlockAlign
BDes = New BufferDescription '初始化描述
BDes.PrimaryBuffer = True '设置为主缓冲区
BDes.Control3D = True '设置为3D可调整
BDes.Format = FMT '设置声音格式
Buff = New Buffer(BDes, Dev) '使用描述初始化主缓冲区
Lis = New Listener3D(Buff)
LisS = Lis.AllParameters
Dim LPos As Vector3
Dim LVel As Vector3
LPos.X = 0
LPos.Y = 0
LPos.Z = 0
LVel.X = 0
LVel.Y = 0
LVel.Z = 0
LisS.Position = LPos
LisS.Velocity = LVel
Lis.AllParameters = LisS
'加载声音
SBDes.Guid3DAlgorithm = DSoundHelper.Guid3DAlgorithmHrtfLight '设置品质为高
SBDes.Control3D = True '设置为3D可调整
SBuff = New SecondaryBuffer("D:\魔兽争霸2\DRIVERS\DIGTEST.WAV", SBDes, Dev) '使用描述初始化二级缓冲区
B3D = New Buffer3D(SBuff) '使用二级缓冲区初始化3D缓冲区
B3DS = B3D.AllParameters
B3DS.Mode = Mode3D.HeadRelative
B3D.AllParameters = B3DS
TextBox1.Text = B3D.MaxDistance.ToString
TextBox2.Text = B3D.MinDistance.ToString
TextBox3.Text = Lis.RolloffFactor.ToString
TextBox4.Text = Lis.DopplerFactor.ToString
TextBox5.Text = B3D.ConeAngles.Inside.ToString
TextBox6.Text = B3D.ConeAngles.Outside.ToString
SBuff.Play(0, BufferPlayFlags.Looping) '声源开始发声
End Sub
Private Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox1.Click
End Sub
Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
Dim Pos As Vector3
Dim Vel As Vector3
Pos.X = (e.X - PictureBox1.Width / 2) / 200
Pos.Y = 0
Pos.Z = (e.Y - PictureBox1.Height / 2) / 200
Vel.X = 0
Vel.Y = 0
Vel.Z = 0
B3D.Position = Pos '设置声源的位置
B3D.Velocity = Vel '设置声源的速度
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim TS As Buffer3DSettings
Dim LS As Listener3DSettings
TS = B3D.AllParameters
LS = Lis.AllParameters
TS.MaxDistance = Val(TextBox1.Text) '设置最大距离
TS.MinDistance = Val(TextBox2.Text) '设置最小距离
LS.RolloffFactor = Val(TextBox3.Text) '设置衰减因子
LS.DopplerFactor = Val(TextBox4.Text) '设置多普勒因子
TS.InsideConeAngle = Val(TextBox5.Text) '设置内锥角
TS.OutsideConeAngle = Val(TextBox6.Text) '设置外锥角
B3D.AllParameters = TS
Lis.AllParameters = LS
End Sub
End Class
好了~~可以体验下3D的魅力了,注意,一定要是单声道声音!
恩,下次是传说中的声音特效。