|
DirectSound!
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
Dim dx As New DirectX7
Set ds = dx.DirectSoundCreate("")
ds.SetCooperativeLevel Me.hwnd,
DSSCL_PRIORITY
You will have to create your
DirectSound and DirectX7 objects and then
create a DirectSound instance and assign it
to the DirectSound object. In this case
ds is our DirectSound object and we use
our DirectX7 object, dx, to create a
DirectSound instance. Once this is done we
need to assign the appropriate cooperative
level. I always use the "Priority" level
since it gives my application exclusive
access to the sound hardware. To assign the
cooperative level use the "SetCooperativeLevel"
method of the DirectSound object. For the
first argument you must pass the handle to
the window for which you are assigning this
cooperative level, for the second argument
you have to pass one of the cooperative
level constants, in this case I use "DSSCL_PRIORITY".
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
DSBuffer will contain our
wave data (it can contain other types of
data, but waves are good enough for me) and
we will need to set up the DSBUFFERDESC and
WAVEFORMATEX objects in order to
appropriately fill the buffer with data.
Using the DSBUFFERDESC,
DSBufferDescription, we can specify the
various capabilities we would like our
buffer to have by assigning the buffer
description's ".lFlags". There are four
capabilities that I utilize:
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!
DSBufferDescription.lFlags =
DSBCAPS_CTRLPOSITIONNOTIFY Or
DSBCAPS_CTRLFREQUENCY Or DSBCAPS_CTRLPAN
Or DSBCAPS_CTRLVOLUME
With DSFormat
.nFormatTag = WAVE_FORMAT_PCM
.nChannels = 2
.lSamplesPerSec = 22050
.nBitsPerSample = 16
.nBlockAlign = .nBitsPerSample / 8 *
.nChannels
.lAvgBytesPerSec = .lSamplesPerSec *
.nBlockAlign
End With
So now we've set up our
DSBUFFERDESC and WAVEFORMATEX objects and we
can proceed to load the sound buffer,
DSBuffer, with data:
Set DSBuffer =
ds.CreateSoundBufferFromFile(App.Path &
"\WaveFile.WAV", DSBufferDescription,
DSFormat)
Alternatively, if we would like to
load the wave from a resource we could do
this:
Set DSBuffer =
ds.CreateSoundBufferFromResource("",
"WaveFile", DSBufferDescription,
DSFormat)
Now, since we created this buffer
using the DSBCAPS_CTRLPOSITIONNOTIFY flag we
MUST set a notification position within it.
If your program does not need to be notified
when a sound has finished or reached a
certain way-point, then simply omit the
DSBCAPS_CTRLPOSITIONNOTIFY flag and ignore
the following code:
Dim DSPosition(0) As
DSBPOSITIONNOTIFY
Dim DSNotification as Long
DSNotification =
dx.CreateEvent(FormObject)
DSPosition(0).hEventNotify =
DSNotification
DSPosition(0).lOffset = DSBPN_OFFSETSTOP
DSBuffer.SetNotificationPositions 1,
DSPosition()
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.
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
You must add the "Implements
DirectXEvent" to the header of the
FormObject's code. This will allow you
to place a new event in the form,
DirectXEvent_DXCallback. This event will be
triggered whenever one of your wave files
reaches an offset value for which you have
assigned an event. When
DirectXEvent_DXCallback is triggered, the
eventid variable created will contain
the event handle used when the event was
created. Use this to determine WHICH of your
wave files triggered this event and then
take appropriate action.
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
That's all there is to it! You
just use the DSBuffer.Play method and pass
the DSBPLAY_DEFAULT constant to play the
sound. If you want your sound to loop:
DSBuffer.Play DSBPLAY_LOOPING
That's it! Stopping the sound
during playback is also a simple matter:
Well, actually, this is more like
PAUSING since you haven't reset the position
of the playback, all you've done is stop the
audio from continuing. To reset the position
to zero, just use the SetCurrentPosition
method:
DSBuffer.SetCurrentPosition 0
So, now you know how to load,
play, pause, and stop your sound buffer.
There are a few fun things left to learn,
however. Remember when we set the
DSBUFFERDESC object back at the start? We
included the ability to alter the frequency,
pan, and volume of our buffer. Since we
included those flags we can use the
following methods:
Const LeftPan = -10000
Const RightPan = 10000
Const CenterPan = 0
Const MaxVolume = 0
Const MinVolume = -10000
Const MaxFrequency = 100000
Const MinFrequency = 100
Dim Freq as Long
Dim Pan as Long
Dim Vol as Long
Freq = DSBuffer.GetFrequency()
DSBuffer.SetFrequency Freq - 1
Pan = DSBuffer.GetPan()
DSBuffer.SetPan Pan - 1
Vol = DSBuffer.GetVolume()
DSBuffer.SetVolume = Vol - 1
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.
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:
|