Beginning Game Audio Programming [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Beginning Game Audio Programming [Electronic resources] - نسخه متنی

Mason McCuskey

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
توضیحات
افزودن یادداشت جدید





PROGRAMMING 3D SOUND

Once you understand the 3D sound concepts, programming 3D sound using DirectX Audio isn't terribly difficult. Here's how to enhance the audio engine to support 3D sound.


C3DSoundListener


The listener is contained in a new class, C3DSoundListener. Here's the class definition:


class C3DSoundListener
{
public:
friend class CAudioManager;
virtual ~C3DSoundListener();
void SetPosition(D3DVECTOR position) {
m_Props.vPosition = position; RefreshProps();
}
D3DVECTOR GetPosition() { return(m_Props.vPosition); }
void SetVelocity(D3DVECTOR Velocity) {
m_Props.vVelocity = Velocity; RefreshProps();
}
D3DVECTOR GetVelocity() { return(m_Props.vVelocity); }
void SetOrient(D3DVECTOR OrientTop, D3DVECTOR OrientFront) {
m_Props.vOrientTop = OrientTop;
m_Props.vOrientFront = OrientFront;
RefreshProps();

}
D3DVECTOR GetOrientTop() { return(m_Props.vOrientTop); }
D3DVECTOR GetOrientFront() { return(m_Props.vOrientFront); }
void SetDistanceFactor(float f) {
m_Props.flDistanceFactor = f; RefreshProps();
}
float GetDistanceFactor() { return(m_Props.flDistanceFactor); }
void SetRolloffFactor(float f) {
m_Props.flRolloffFactor = f; RefreshProps();
}
float GetRolloffFactor() { return(m_Props.flRolloffFactor); }
void SetDopplerFactor(float f) {
m_Props.flDopplerFactor = f; RefreshProps();
}
float GetDopplerFactor() { return(m_Props.flDopplerFactor); }
protected:
// only accessible to friends (i.e., CAudioManager)
C3DSoundListener();
CAudioManager *m_Manager;
void RefreshProps();
DS3DLISTENER m_Props;
IDirectSound3DListener8 *m_Listener;
};

There are a lot of accessor functions here for the members of the m_Props structure. The m_Props structure is of type DS3DLISTENER, which is a DirectX-supplied structure that contains all of the properties for a listener.

Other than the m_Props member and all of its associated accessor functions, all this C3DSoundListener class contains is a protected RefreshProps method, a pointer to the CAudioManager, and a pointer to the DirectSound listener interface, m_Listener.

Notice that each set accessor method calls RefreshProps after it sets the appropriate structure value. The RefreshProps method sends the m_Props structure to DirectX Audio by calling the SetAllParameters method of the listener interface:


void C3DSoundListener::RefreshProps()
{
HRESULT hr = m_Listener->SetAllParameters(&m_Props, DS3D_DEFERRED);
ThrowIfFailed(hr, "Error calling SetAllParameters");
}

The only interesting thing in this little method is the use of the DS3D_DEFERRED flag. This flag tells DirectX Audio to hold off on recalculating the mixing buffers until the CommitDeferredSettings method of the listener interface is called.


Changes to CAudioManager


Since there can only be one listener, I've structured the audio engine so that the audio manager contains and keeps track of the global listener instance. There's a new m_Listener member of CAudioManager, which is a C3DSoundListener. Other code accesses the listener class by calling the GetListener accessor function.

CAudioManager is also responsible for getting the listener interface in the first place. Once it gets this interface, it gives it to the new listener instance by setting its m_Listener member.

Here's how it looks in code—this new code is part of the Init method of CAudioManager:


// get the listener buffer
IDirectMusicAudioPath8 *defaudiopath = NULL;
IDirectSound3DListener8 *listener;
hr = GetPerformance()->GetDefaultAudioPath(&defaudiopath);
ThrowIfFailed(hr, "Can't get default audio path!");
hr = defaudiopath->GetObjectInPath(0, DMUS_PATH_PRIMARY_BUFFER,
0, GUID_NULL, 0, IID_IDirectSound3DListener8, (LPVOID*) &listener);
ThrowIfFailed(hr, "can't get listener on default audio path!");
// tell our listener object to use this listener interface
GetListener().m_Listener = listener;
GetListener().RefreshProps();
Commit3DSoundParameters();
SAFE_RELEASE(defaudiopath);

The code just grabs the listener interface by getting an interface to the default audiopath, then calling the GetObjectInPath method of that interface. In DirectX Audio, there's only one listener interface, so it doesn't matter which audiopath you get it from.

Once it has the listener interface, the code hands it to the C3DSoundListener object, then calls the RefreshProps method of that object, so that the object has a chance to send its default values to DirectX Audio. Also, because the listener's RefreshProps method set the parameters using the deferred flag, the code immediately calls Commit3DSoundParameters to force DirectX Audio to commit the newly-sent parameters and recalculate the mix. Once all that's complete, the code cleans up its mess by releasing the audiopath interface. Note that the listener interface isn't released; the listener object now owns that interface, and will release it when the listener's destructor is called.

The Commit3DSoundParameters method is a one liner:


void CAudioManager::Commit3DSoundParameters() {
GetListener().m_Listener->CommitDeferredSettings();
}

This method just calls the CommitDeferredSettings method of the listener interface.


C3DSoundEffect


One new class down, one to go. C3DSoundEffect represents a 3D sound buffer:


class C3DSoundEffect : public CDirectMusicSegment
{
public:
C3DSoundEffect(CAudioManager *mgr);
virtual ~C3DSoundEffect();
// override segment's function to play out our own audiopath
virtual bool Play() {
return(CDirectMusicSegment::Play(NULL, false, m_AudioPath));
}
virtual bool Play(CSoundInstance *newinst, bool control) {
return(CDirectMusicSegment::Play(newinst, control, m_AudioPath));
}
virtual bool PlayAsPrimary(CSoundInstance *newinst) {
return(CDirectMusicSegment::Play(newinst, true, m_AudioPath));
}
// properties
// accessor functions snipped to save space
protected:
void RefreshProps();
DS3DBUFFER m_Props;
IDirectMusicAudioPath8 *m_AudioPath;
IDirectSound3DBuffer8 *m_3DBuffer;
};

Again, most of it is just accessor functions for the m_Props structure (which for this class is of type DS3DBUFFER). However, there are some new things.

First, notice that C3DSoundEffect derives from CDirectMusicSegment, but that it overrides CDirectMusicSegment's Play methods with its own. I did this because, unlike a normal segment, a 3D sound buffer needs to explicitly provide a 3D audiopath every time it plays a sound.

This meant that I needed to go into CDirectMusicSegment and change things around so that you could pass in your own audiopath pointer, and if you did, the CSoundInstance that eventually was created wouldn't release the audiopath pointer. I'm not going to go through those changes line by line, because they're fairly self-explanatory, but you do need to know that they occurred.

This means that the C3DSoundEffect class needs to create and manage its own audiopath. Here's how the audiopath creation looks (this is taken from the C3DSoundEffect constructor):


// create our 3D audiopath
HRESULT hr = m_Manager->GetPerformance()->CreateStandardAudioPath(
DMUS_APATH_DYNAMIC_3D, CAudioManager::NUMCHANNELS,
TRUE, &m_AudioPath);
ThrowIfFailed(hr, "Couldn't create 3D audio path!");
// get our 3D sound buffer from that new audiopath
hr = m_AudioPath->GetObjectInPath(DMUS_PCHANNEL_ALL,
DMUS_PATH_BUFFER, 0, GUID_NULL, 0, IID_IDirectSound3DBuffer8,
(LPVOID*)&m_3DBuffer);
ThrowIfFailed(hr, "Couldn't get buffer from audio path!");

This code creates a standard 3D audiopath (note the use of the DMUS_APATH_DYNAMIC_3D flag), then queries it for an interface to the 3D DirectSound buffer contained within it.

C3DSoundEffect also has a RefreshProps method that's identical to the listener's, except it's operating on a DirectSound buffer instead of a listener.

/ 127