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

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

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

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

Mason McCuskey

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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





LOADING AND PLAYING MIDI FILES

As I mentioned in the beginning of this chapter, DirectMusic tries to treat MIDI music and WAV files the same way. Both are loaded into segments through the Loader. The good news is that the code to load a MIDI song from disk, memory, or resources into a segment is exactly the same as the code to load a WAV file.


Enhancing the Audio Manager


The bad news is that the audio manager's load routines need to get stirred up a little to accommodate this. The audio manager allows you to load WAVs through its LoadSound methods. A good design would be to make it load MIDI files by creating three new LoadMIDI methods (one for loading from disk, one for loading from memory, and one for loading from resources).

You don't want to just cut and paste the LoadSound routines and change their names. You should never cut and paste large code blocks because whenever you do so, you needlessly increase the amount of code that you need to deal with. A better solution is to isolate the common code into three core functions, and then make both the LoadSound and LoadMIDI routines call those functions.

The three core functions are called LoadSegmentFromDisk, LoadSegmentFromMemory, and LoadSegmentFromResource. They take different arguments. Taking just one as an example, here's the new LoadSegmentFromDisk:


void CAudioManager::LoadSegmentFromDisk(CDirectMusicSegment *dest, std::string
filename)
{
HRESULT hr;
// convert filename to wide-string
WCHAR widefilename[MAX_PATH];
DXUtil_ConvertGenericStringToWide( widefilename, filename.c_str());
// tell loader to load this file
hr = m_Loader->LoadObjectFromFile(
CLSID_DirectMusicSegment,
IID_IDirectMusicSegment8,
widefilename,
(void**) &dest->m_Segment);
ThrowIfFailed(hr, "LoadObjectFromFile failed.");
}

That code contains nearly everything that the disk-loading overload of the LoadSound method used to contain. What's missing is the class instantiation; instead of creating a new class, LoadSegmentFromDisk is passed a pointer to one. This is because of the new design. LoadSound creates CSoundEffect classes and passes them to LoadSegmentFromDisk, whereas LoadMIDI creates CMIDIMusic classes. Thanks to polymorphism, LoadSegmentFromDisk doesn't know the difference between a CMIDIMusic class and a CSoundEffect class; it uses their common base class, CDirectMusicSegment.

Here's the new version of LoadSound:


CSoundPtr CAudioManager::LoadSound(std::string filename)
{
CSoundEffect *snd = new CSoundEffect(this);
LoadSegmentFromDisk(snd, filename);
return(CSoundPtr(snd));
}

As you can see, now LoadSound just creates the class, calls LoadSegmentFromDisk to do the dirty work, and returns a new CSoundPtr. LoadMIDI looks virtually identical:


CSoundPtr CAudioManager::LoadMIDI(std::string filename)
{
CMIDIMusic *music = new CMIDIMusic(this);
LoadSegmentFromDisk(music, filename);
return(CSoundPtr(music));
}

Instead of a CSoundEffect, LoadMIDI creates a CMIDIMusic class, then does exactly the same thing as LoadSound.

The other two LoadSound and LoadMIDI overloads look similar—they just create the appropriate class and pass it to LoadSegmentFromMemory or LoadSegmentFromResource.


Getting a CMIDIMusic Pointer


You might be wondering how to get at the methods specific to CMIDIMusic once you've loaded a MIDI file. For example, you might want to change the tempo. The SetTempo method is contained inside the CMIDIMusic class, but all you have is a pointer to a CSound!

To get out of this jam, use run-time type information. RTTI was designed specifically for cases like this—you have a pointer to a base class but need to know if it's actually a derived class. For example, here's how to get a CMIDIMusic pointer from a CSoundPtr:


CSoundPtr snd = g_AudioMgr.LoadMIDI("test.mid");
CMIDIMusic *music = dynamic_cast<CMIDIMusic *>(snd.Get());
If (music != NULL) {
// snd was actually a CMIDIMusic pointer! Do stuff!
}
else {
// snd wasn't a CMIDIMusic pointer! D'oh!
}

Notice the dynamic_cast keyword. This keyword converts a pointer into a different type. Here the code is getting the pointer by calling snd.Get, and then trying to convert it to a CMIDIMusic pointer. If the dynamic_cast works, the pointer is put into music (note that music and snd still point to the same memory address; they're just different types). If for some reason the dynamic_cast can't convert the pointer (for example, if you've passed in a pointer to a CSoundEffect class), it will set music to NULL.

In this way you can get to the methods in CMIDIMusic or CSoundEffect, given a CSound or CDirectMusicSegment pointer.

/ 127