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

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

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

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

Mason McCuskey

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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





CONTROLLING VOLUME USING DIRECTX AUDIO

Frequently, games need to be able to change the volume of a sound based on game events. This section will teach you how to do that using DirectX Audio.


Audio Paths


In DirectMusic, volume is tied to an object called an audio path. An audio path contains all of the information that influences how a segment is Played, but doesn't necessarily belong as part of the segment itself. The classic example of this is (you guessed it) volume.

It wouldn't make sense for the volume knob to reside in a segment. If it did, and you wanted the same segment to be Played at different volumes, you'd have to duplicate the segment object—a horrible waste of resources. So instead, DirectMusic puts most of the "per-instance" controls inside an object called an audio path.





Tip

The position of a 3D sound is also stored as part of the audio path object, but you'll learn about that later.


Currently the audio engine has been using the "default audio path" for all the sounds it Plays. The default audio path is the one you created when you called InitAudio. This is fine for simple sound Playback, but if you want to change the volume of a particular sound, you'll run into trouble. All of the sounds use the same audio path, so when one sound changes the volume of that audio path, it indirectly changes the volume of all the other sounds (see Figure 5.2).


Figure 5.2: Sounds that share audio paths also share volume knobs.

There are a couple of different solutions to this, but they both involve using multiple audio paths. Some audio programmers like to set up a "pool" of audio paths; when a sound is Played, their code chooses an empty audio path and assigns the sound to it. This is elegant for a number of reasons—for starters, it gives the programmer the ability to easily see and manipulate all the audio paths, without having to iterate through all of the Playing sounds.

The alternative method, and the method I've implemented in the audio engine, is to create a new audio path each time a sound is Played. Once the sound has finished Playing, the audio path sticks around until it's released. Because audio paths are relatively lightweight classes, this is okay, provided you can deal with a little allocation inside your game logic. I think it's significantly simpler to code and teach than the first method, which is what really clenched the decision for me to use it in this book.

To accommodate these audio paths requires a few changes to the Play method of CSound. Here's the new code:


bool CSound::Play(CSoundInstance *newinst)
{
HRESULT hr;
if (NULL == m_Segment || NULL == m_Manager) {
if (newinst) newinst->UnInit();
return(false);
}
static CSoundInstance dummy;
if (NULL == newinst) newinst = &dummy;
IDirectMusicPerformance8 *perf = m_Manager->GetPerformance();
IDirectMusicAudioPath8 * pPath = NULL;
IUnknown *pConfig;
if (SUCCEEDED(m_Segment->GetAudioPathConfig(&pConfig)))
{
// this has an embedded audio path configuration
hr = perf->CreateAudioPath(pConfig, TRUE, &pPath);
pConfig->Release();
ThrowIfFailed(hr, "Couldn't create audiopath.");
}
else {
// no config... make our own!
hr = perf->CreateStandardAudioPath(DMUS_APATH_DYNAMIC_STEREO,
CAudioManager::NUMCHANNELS, TRUE, &pPath);
ThrowIfFailed(hr, "Couldn't create standard audiopath.");
}
m_Segment->Download(perf);
// initialize the sound instance (set volume, etc.)
// this will also set it Playing.
newinst->Init(this, pPath);
return(true);
}

This code starts out the same, but once it gets the performance and puts it in perf, things change. First, it looks in the segment to see if there's a configuration for an audio path. The config object is like audio path DNA, so if the code finds a configuration, all it must do to create the audio path is give that configuration to CreateAudioPath, which puts the interface of a new audio path inside the pPath variable. Wave files don't have audio path configurations inside them, but music segments authored in DirectMusic Producer usually do.

If there's no configuration embedded in the segment, the code creates a new standard audio path, and puts that interface in pPath. So either way, by the time the code downloads the segment, it has a valid path for this particular sound instance inside pPath. In the final line, the code hands the pPath variable to the sound instance's Init method. Here's the new code for Init:


void CSoundInstance::Init(CSound *sound, IDirectMusicAudioPath8 *path)
{
HRESULT hr;
m_AudioPath = path;
m_Valid = true;
// set volume based on sound's volume
SetVolume(sound->GetVolume());
// now that everything's set, go ahead and Play it.
hr = GetAudioManager()->GetPerformance()->PlaySegmentEx(
sound->GetSegment(), NULL, NULL,
DMUS_SEGF_SECONDARY, 0,
(IDirectMusicSegmentState **)&m_SegmentState,
NULL, path);
ThrowIfFailed(hr, "PlaySegmentEx failed.");
}

The Init method squirrels away the audio path interface into the m_AudioPath member. It then passes that interface to PlaySegmentEx, as the last parameter. This tells DirectMusic to Play the sound using that audio path.

This Init method uses the m_AudioPath interface inside SetVolume when it sets the sound's volume. You'll learn about that method next.


Segment Specific Volume


As mentioned earlier, you control a sound's volume through its audio path. Here's how to get and set the volume of an audio path.

Setting the Volume


The IDirectMusicAudioPath8 interface has a method called SetVolume. This method takes two arguments—a volume (more precisely, a decibel attenuation) specified in hundredths of a decibel, and a duration over which the volume change occurs (making volume fades really easy). The SetVolume method of CSoundInstance uses this SetVolume method of the audio path to change the volume of a Playing sound.

Atop this design, I've built some more advanced functionality into the audio engine. If you look in the CSound class declaration, you'll notice that there's also a SetVolume method there now. This allows you to specify a default volume for new instances of this particular sound. CSound doesn't have to do much to make this happen—it just remembers whatever value you specify. The real work happens in the CSoundInstance::Init method. Notice the line:


SetVolume(sound->GetVolume());

This sets the volume of the audio path to the current volume of the CSound.





Tip

Note the order in which CSoundInstance::Init does things. In particular, notice that it sets the volume on the audio path before it starts Playing the sound. This guarantees that the sound will Play from the get-go with the correct volume. Had the code set the volume after the call to PlaySegmentEx, there might have been a slight instant in which the user heard the sound at full volume. The order in which you do things is important—keep this in mind.


Querying the Volume


Querying the volume of an audio path is similarly easy—just call the GetVolume method of the audio path interface you're interested in. GetVolume takes one parameter: a pointer to a long integer, which it fills with the current volume on the audio path.

Another Volume Knob


Of course, the audio path volume isn't the only volume you can adjust. The underlying DirectSound buffers (remember those?) also have volume knobs, accessible via the SetVolume and GetVolume methods of IDirectSoundBuffer8.

I find it easiest to work with only one volume knob, and leave all the other ones alone. Adjusting both the DirectSound buffer's volume and the segment's volume can make things confusing. I usually just go with the segment volume.



Why Such a Funny Volume Range?


Most programmers, used to working with ranges that start at zero and end at one or some other positive number, are completely perplexed as to why DirectMusic's volume control has the range –9,600 to 0.

First of all, the range is specified in hundredths of decibels. That is, –100 is –1 decibel, and –9,600 is –96 decibels. Okay, so that makes it a little more accurate, but why the negative numbers? In the analog recording days of yore, audio engineers measured the quality of their equipment in terms of negative decibels. A rating of zero was impossibly perfect because it meant that the equipment had not lost anything in the process of recording the sound. As the rating dropped below zero, the shoddiness of the equipment rose.

An undamaged human ear has a hearing range of about 120 decibels. That means that if you turned your speakers up loud enough to where permanent hearing damage would result if you Played a sound with volume 0, then you could just barely hear a sound when Played at volume –120 decibels. Of course, a more sensible range would be slightly less than that, say, –96 to 0 decibels.

This method of only reducing the volume of sounds is called attenuation. Virtually all audio hardware today implements volume control using attenuation; in other words, the source samples are never made louder, only reduced.




Global Volume


There's yet another volume knob you can adjust inside DirectMusic. DirectMusic lets you specify the volume of the performance as a whole. So, how do you do it? Of course, it wouldn't be as easy as calling the SetVolume method of IDirectMusicPerformance8—that method, curiously enough, doesn't exist.

Instead, you have to set the volume via a call to the SetGlobalParam method of the performance interface. Pass in GUID_PerfMasterVolume as the first parameter, the volume you'd like as the second parameter, and the size of a long as the third parameter.

This performance volume knob uses a different range than the audio path. The range depends on the software synthesizer, and for Microsoft's stock synth, the volume here is specified in hundredths of a decibel—anywhere from –200 to +20 decibels. That means the range is actually –20,000 to 2,000, instead of the audio path's –9,600 to 0 range.

I've added GetVolume and SetVolume methods to CAudioManager, but because they're self-explanatory, I won't waste paper dissecting them line by line.


DirectX Volume Control Wrap Up


I hope you've found this "tour of DirectMusic volume knobs" useful, interesting, and fascinating. Like I said before, powerful APIs make the hard stuff easy but the easy stuff hard. Fortunately, the complexity of DirectMusic's volume controls is nothing compared to the Mixer API, which you'll tackle next.

But before you plow forward, stop and explore the Ch5p4_DMusicVolume sample program, which illustrates per-sample and global volume control.

/ 127