You know what it would be like to play a game without sound? BORING, that's what! It'd be almost as boring as programming in C++! Heh. So take heed, Lucky is about to render you DirectSound-literate. Learn well!
Lets jump right in, shall we? As always, we must first tackle the Initialization steps:
Dim ds As DirectSound
ds.SetCooperativeLevel Me.hwnd, DSSCL_PRIORITY
Dim dx As New DirectX7Set ds = dx.DirectSoundCreate("")
Ok, now that we're initialized at the proper cooperative level we have to set up a buffer and the appropriate buffer descriptions:
Dim DSBuffer As DirectSoundBuffer
Dim DSBufferDescription As DSBUFFERDESC
Dim DSFormat As WAVEFORMATEX
DSBCAPS_CTRLFREQUENCY - This will give us the ability to modify a buffer's output frequency
DSBCAPS_CTRLPAN - This gives us the ability to alter a sound's pan (ie. Which speaker it is coming out of)
DSBCAPS_CTRLVOLUME - Volume, hm... what could this do?
DSBCAPS_CTRLPOSITIONNOTIFY - A useful one. This allows us to trigger events at various locations within a sound's playback.
The WAVEFORMATEX object has a number of items we need to assign:
nFormatTag - What data format are we using? WAVES! So pass the constant WAVE_FORMAT_PCM.
nChannels - How many channels do you want to use? 1 = mono, 2 = stereo.
lSamplesPerSec - How many cycles/second (Hertz) will you use? Standard is 22050.
nBitsPerSample - What "bit depth" will you use? Standard is 16.
nBlockAlign - Just leave it at: "nBitsPerSample / 8 * nChannels" and you'll be fine :)
lAvgBytesPerSec - Set this one to: "lSamplesPerSec * nBlockAlign" and you'll be fine again!
With DSFormat
DSBufferDescription.lFlags = DSBCAPS_CTRLPOSITIONNOTIFY Or DSBCAPS_CTRLFREQUENCY Or DSBCAPS_CTRLPAN Or DSBCAPS_CTRLVOLUME
.nFormatTag = WAVE_FORMAT_PCM
End With
.nChannels = 2
.lSamplesPerSec = 22050
.nBitsPerSample = 16
.nBlockAlign = .nBitsPerSample / 8 * .nChannels
.lAvgBytesPerSec = .lSamplesPerSec * .nBlockAlign
Set DSBuffer = ds.CreateSoundBufferFromFile(App.Path & "\WaveFile.WAV", DSBufferDescription, DSFormat)
Set DSBuffer = ds.CreateSoundBufferFromResource("", "WaveFile", DSBufferDescription, DSFormat)
Dim DSPosition(0) As DSBPOSITIONNOTIFY
Dim DSNotification as LongDSNotification = dx.CreateEvent(FormObject)
This will take some explaining :) First, we have to define two new variables. DSPosition(0) will be our DSBPOSITIONNOTIFY
array with which we can set the locations within the wave data where we would like events to be triggered. DSNotification
will hold the event handle returned by the CreateEvent method.
DSPosition(0).hEventNotify = DSNotification
DSPosition(0).lOffset = DSBPN_OFFSETSTOP
DSBuffer.SetNotificationPositions 1, DSPosition()
Using the dx.CreateEvent method we make an event and store its handle in DSNotification. We have to pass a form object when we use the CreateEvent method. The form that we pass will be the form within which the event is created (more on this later, don't get flustered!). Now we store the event handle in DSPosition(0).hEventNotify and assign a "position offset" to DSPosition(0).lOffset. Here I've used DSBPN_OFFSETSTOP which means that the event will be generated when the wave file reaches the end. Once the DSPosition object has been filled we can make a call to the DSBuffer.SetNotificationsPosition method. The first argument we pass is simply the number of positions to which we are assigning events (in this case we're only assigning one event, to be triggered when the wave has played to the end). The second argument requires a DSBPOSITIONNOTIFY array so we will pass our DSPosition() object.
NOTE: You MUST create an ARRAY for the DSBPOSITIONNOTIFY variable since the SetNotificationPositions method will only accept arrays. In our example above our array contains only a single position description, but you could assign multiple events to multiple positions within a wave file using a larger array.
The FormObject that we passed to the CreateEvent method will need some modifications made to its code as well:
Implements DirectXEvent
Private Sub DirectXEvent_DXCallback(ByVal eventid As Long)
End Sub
An example will help to clarify: In the code above we created an event for the DSBuffer object and it was assigned an event handle that we stored in DSNotification. When we play our DSBuffer and the end of the wave sound is reached the DirectXEvent_DXCallback subroutine will be triggered within the form FormObject. The eventid generated by the DirectXEvent_DXCallback will be identical to the event handle we've stored in DSNotification. Get it?
PHEW! All that just to load a sound file into a sound buffer! ONWARD!
Now you're probably anxious to actually PLAY the sound so you can test if things are working correctly. Well, it gets easier from here on in, don't worry :)
DSBuffer.Play DSBPLAY_DEFAULT
DSBuffer.Play DSBPLAY_LOOPING
DSBuffer.Stop
DSBuffer.SetCurrentPosition 0
Const LeftPan = -10000
Dim Freq as Long
Pan = DSBuffer.GetPan()
Vol = DSBuffer.GetVolume()
Const RightPan = 10000
Const CenterPan = 0
Const MaxVolume = 0
Const MinVolume = -10000
Const MaxFrequency = 100000
Const MinFrequency = 100
Dim Pan as Long
Dim Vol as LongFreq = DSBuffer.GetFrequency()
The GetFrequency method returns the frequency, in Hertz, of the wave currently stored in the buffer. Using the SetFrequency method
we can alter the frequency within the limits set out by the MaxFrequency and MinFrequency constants. The GetPan
method returns the current Pan value of the buffer. Center (ie. Audio split equally between left and right speakers) is at zero,
fully left is -10000, and fully right is 10000. Using SetPan we can alter the pan of the buffer. GetVolume will return the current
volume state of the buffer. Max volume is at zero, min volume is at -10000. Be wary, volume is measured in decibels which utilize
a logarithmic scale. Decreasing the volume slightly using the SetVolume method will have sharply defined effects on the output of
your wave file.
DSBuffer.SetFrequency Freq - 1
DSBuffer.SetPan Pan - 1
DSBuffer.SetVolume = Vol - 1
That's pretty much all there is to know about the basics of DirectSound. Don't forget to clean up your objects when you're done:
Set DSBuffer = Nothing