AUDIO SCRIPTING
Included in DirectX starting with version 8.0, audio scripting provides a powerful way for development teams to parallelize their development process. At first glance, it may seem like overkill to use a scripting language to play sounds, but in reality it's very useful for team game development, because it allows sound designers to work faster and with less dependency on the programmers.
Why Use Audio Scripting?
Before audio scripting, it was difficult for sound designers to work autonomously. Picture this—you are a sound designer, working on a team consisting of several other sound designers and programmers (and artists, animators, producers, and all sorts of other people too). Your job today is to make sure the orc grunts in your game sound good.So you work your magic and you come up with what you think is a pretty good WAV file of an orc grunt. You check it into your asset database, and send a note off to the sound programmer asking him to put your sound in the game. The programmer does so, and gives you back a new build of the game so you can hear your sound.You hear your sound in-game, and that's when you realize it sounds awful. Your orc grunt, which sounded so great in your WAV editor, is playing too soft in the game, and you can barely hear the grunts. So you send an e-mail to the programmer asking him to raise the volume of the orc sound effect. The programmer does so, and makes you a new build of the game.And then you realize that the orc grunt sounds good on the blue orc but that it sounds silly for the red orc to make the exact same grunting sound as the blue orc. So you make a new blue_orc_grunt.wav file and send an e-mail to your programmer, asking him to put this new sound in the game.Do you see what's going on here? Every time you want to change something with the audio, you've got to bug the programmers for a new build. This wastes everybody's time—the programmers don't want to be constantly interrupted, and you don't want to constantly have to interrupt them and wait for a new build.Audio scripting solves this problem by moving the code that plays sound effects out into a script which is loaded at runtime. With this system in place, sound designers no longer have to rely on programmers to compile their changes into the game. They can simply edit a script (text file) and re-start the game to see their changes.
Sidebar: Cycle Time
Professional game development teams live and die by what they refer to as "cycle time." Cycle time is simply the time between two tests, minus any time you spent fixing things.For a programmer, cycle time refers to how long it takes to stop the debugger, re-compile the code, re-launch the debugger, and get back to the place in the game that's having the problem. For the artist or animator, cycle time refers to how long it takes to export the new art asset (texture, model, animation, and so on) and see it inside the game.Obviously, the shorter the cycle time, the more "tweaks per minute" you can have, and the better your end product will be. If you're an artist, and it takes you several steps and several minutes to see your new art inside the game, you may only go through two or three cycles before you decide that it's "good enough" and move on to something else. If, however, your cycle times are short—say you can see your art inside the game just by typing a couple of commands—you're going to be able to go through several rounds of view art/change art, and the game's art will look better because you've spent time perfecting it.So, if you're working in a team, strive to keep everyone's cycle times as short as possible. You can do this by spending time creating WYSIWYG (or, in the case of game audio, "what you hear is what you get") game editors, and by making as many things data-driven as possible. The end result will be a better game and happier teammates.
Of course, the programmer and sound designer still have to agree on the list of available sound effects the game will have. But the programmer no longer needs to be concerned with the details of how those sound effects are played (for example, whether one wav file is used for several sound effects, how loud to play, and so on). The programmer just calls a subroutine defined in an audio script, and the audio script takes care of telling DirectMusic what sound to play, and how.
Tip | Audio scripts aren't particularly useful if you're a lone-wolf developer or if your sound designer also happens to be a programmer and can edit game code directly. However, if you're working in a large team, audio scripts can really help out. |
The Basics of Audio Scripting
Imagine that you're creating a game involving cats (Hey, it worked for the theater—why not for the gaming world as well?). The player controls a hero-cat, and can find powerups that over time causes his cat to grow bigger.
Tip | It's crucial to come up with an appropriate audio interface—that is, the list of routines and variables used in your audio script. Audio scripting works best when there is a well-defined interface between the game code and the audio. In a perfect world, you'd define your audio interface once, and only once. The programmers and sound designers would meet, decide what variables the game code would set, what routines it would call, and then they would both go their separate ways. The audio guys would work on the audio script, and the programmers would work on the rest of the game code.In practice, that never happens, but it's a goal to strive for. A few changes to your audio interface will always occur, but if you find yourself constantly re-inventing your entire interface, you'll also find yourself missing out on the power of audio scripting. |
You're the sound designer for this (revolutionary) game, so it's your job to create the feline sound effects. You and your programmers have decided on a simple audio interface. The game code will keep track of how big the hero's cat is, and will convey that information to you via a single variable named CatSize. There will also be single function, sfxCatMeow, which the game code will call whenever the cat should meow. This sfxCatMeow function should look at the CatSize variable and create a meow of the appropriate power. Ideally, there should be a handful of different meowing sound effects, so that the cat doesn't meow exactly the same over and over again.This situation is specially designed to illustrate the usefulness of audio scripts. With an audio script, the sound designers are free to create as many different meowing sounds as they like, and they're free to organize them in whatever fashion they choose, without having to edit game code. Likewise, the programmers are free from the details of how to make a cat meow—they just call a function in a script, and the right meow comes out the speakers, as if by magic.The following two sections will show, from a sound design and coding perspective, how this audio script is implemented. This audio script will have only one routine and only one variable; keep in mind that an audio script for a real game will have dozens of routines and variables.You'll first learn how to create the script (that is, code the script itself), followed by how to integrate the script into your C++ game code.
The Sound Designer's Perspective
The audio scripting language DirectX uses is based on Visual Basic, so if you've used any of the other VB variants (Active Server Pages, Office macros, and so on), you already know most of what you need. If you haven't done anything with VB before, you'll probably want to read some online tutorial to pick up the basic flow control and syntax (see some of the links I've provided on your CD).You can create AudioVB scripts using DirectMusic Producer, but it's a little more involved than just creating a new script object and typing your script into the editor. This is because you can't play WAV files directly from an AudioVB script—you can only play segments. So, you need to create a WAV segment for each sound effect you want to play through the script.Think of a WAV segment as a segment that contains WAV files instead of patterns. In this chapter, your WAV segments won't have groove levels, they won't depend on styles, and they won't play patterns. Instead, they will generate their sound by using a WAV track. I think of segments with a WAV track as DirectMusic shortcuts; sure, you could create a style and a bunch of patterns, create a style track and groove track in your segment, and get your sound effects to play that way. That's useful if you're going to be constructing complex sound effects from simple WAV elements. But if you just want to play a simple sound effect, you don't have to deal with all that. You can just embed a WAV file directly into your segment and be done with it.
Tip | AudioVB scripts aren't limited to just playing sound effects (though that's all you will learn about here). You can also use them for playing regular segments, changing groove levels, playing motifs—virtually anything you can manipulate with DirectMusic Producer, you can manipulate in an audio script. |
Creating Sound Effects Segments
That's what you're going to learn now. Fire up DirectMusic Producer and create a new project (I called mine CatProject). You'll need an audio script and two segments. Name the script



Tip | Instead of clicking 29 times to enable/disable each variation, you can toggle the entire set of variations by clicking the narrow button to the left of all the variation buttons (see Figure 12.1). ![]() Figure 12.1: Clicking the narrow button on the left will enable/disable the entire variation set. |
The segment is now beginning to look a lot like a pattern. Notice that your new wave track (a.k.a. Wave Part) has variation buttons just like a pattern does. Disable everything except variation 1, move your cursor to the beginning of the track, and hit Insert. The wave properties, shown in Figure 12.2, will appear. Choose CatProject:BigCat1 to tell DirectMusic that it should play this wave file for variation 1 (note that only WAV files that are part of a project appear in this list; that's why you had to import them before getting here). If all goes well, you should see a graphical representation of your WAV file depicted at the start of the track.

Figure 12.2: The wave properties window.
Follow the same process to make the







Figure 12.3: The wave track properties.
If you play the segment using your toolbar, you should now hear



Figure 12.4: The final BigCat segment,with both variations active.
Duplicate the same recipe to create a SmallCat segment that randomly plays either


Authoring a Simple Script
With your segments set up, you're now ready to create the audio script itself. Begin by clicking the New toolbar button and selecting Script from the dialog that appears. Rename the new script catsfx.There's one thing you need to do before you start writing the code for the audio script—you need to tell DirectMusic which objects of your project your script will reference. The script you're about to input will use the two segments SmallCat and BigCat, so you need to add those two segments to the script's references. Open the script content node underneath your script in your treeview, and right-click on Reference Runtime. Add



Figure 12.5: Adding references to an audio script.
Caution | It's crucial that you add references to all objects your audio script references. If you don't, your script will not work. |
Once you've got your references set up, go back to the script editor (double-click on your catsfx script) and enter the AudioVB code shown next:
dim CatSize
sub sfxCatMeow
if CatSize < 10 then
SmallCat.Play(IsSecondary)
else
BigCat.Play(IsSecondary)
end if
end sub
Watch your syntax—AudioVB is different than C++ (for example, putting semicolons at the end of statements in AudioVB will really screw you up).As for the script itself, it really is that easy—it declares one variable, CatSize, and one subroutine, sfxCatMeow. The sfxCatMeow subroutine looks at the CatSize variable (which the game will set). If it's less than ten, it plays the SmallCat segment; otherwise, it plays the BigCat segment. Note the use of the IsSecondary flag, which tells DirectMusic the segment should be played as a secondary segment (usually you'll want your sound effects to be secondary segments so they don't usurp control from any dynamic background music that might be playing).
Tip | When editing your script, you can use DirectMusic Producer to help check for syntax errors. If you hit the refresh button above the routines list to the right of your editing area, and the list box gets filled in with the names of your subroutines, you'll know your script has no syntax errors. Conversely, if you don't see your subroutines and variables populated when you hit refresh, check your syntax! |
Save this script, save your project, and then save everything in Runtime format, and you're done! Put the runtime files into the working directory of the Chapter 12 sample program—the folder named Ch12p1_AudioScripts. You can do this by saving everything directly to that folder, or by copying the runtime files once they're saved inside your project folder. Alternatively, you could also write code to tell DirectX to search in a different directory—by default it searches in the current directory, which, when you run your sample program through DevStudio, is Ch12p1_AudioScripts.Obviously, a real game would have a more complex audio script. A real game would probably have functions for each sound effect, as well as functions to manipulate the dynamic background music, play motifs, and do whatever else the game demanded. It's entirely up to you to decide how to organize all these subroutines. You can have as many different script files, variables, and subroutines as you want, though a good rule is to use one script file for each level of your game.
Tip | Notice in the audio script that the name of the subroutine is prefixed with sfx. It's a good idea to come up with some sort of standard naming convention for your audio scripts—for example, decree that any subroutine that's concerned with playing a sound effect should start with sfx. Creating (and following!) standard naming conventions can help keep large scripts organized. |
The Developer's Perspective
Once you've created your segments and your script, and saved them out in runtime format, you can turn your attention to writing the game code that will interface with the audio script.DirectX Audio provides a very simple, yet 100% complete, interface to your audio scripts. Essentially, after initializing a script interface, you can ask that interface for a list of subroutines and variables, you can set and retrieve variable values, and you can call subroutines. That's really all you have to do, and most of the time you don't even need to get the list of subroutines and variable names, because just hard-coding the names of the subroutines and variables works just fine.So, this makes for a short list of code enhancements to perform. The following sections will guide you through adding audio scripting support to the audio engine. Most of this support will come via a new CAudioScript class, backed up by a few new CAudioManager methods. But before you can dive into CAudioScript or CAudioManager, you need to create a new smart pointer.
A New, Slightly Smarter Pointer Class
Up until now, you've gotten away with having only one smart pointer class, CSoundPtr. That's because everything you've learned so far can, at its most basic level, be treated as something you can play, stop, and set the volume on. You can't really play an audio script like you can a segment or a wave file.So, it doesn't make sense to derive your new CAudioScript class from CSound. CAudioScript doesn't derive from anything, because it's unlike anything you have currently written. This means that when you ask CAudioManager to load an audio script, you're not going to get a CSoundPtr back.But you have to get something back! Sure, you could just write the load routines and make them return a CAudioScript pointer, but it's very simple to create a new smart pointer (derived from the CRefCountPtr template) specifically designed for CAudioScript. Here's how it looks:
// smart pointer to CAudioScript
class CAudioScriptPtr : public CRefCountPtr<CAudioScript>
{
public:
explicit CAudioScriptPtr(CAudioScript* p = NULL);
void Release();
};
That's not so bad—the heavy lifting is done by CRefCountPtr<CAudioScript>. All CAudioScriptPtr needs is an explicit constructor and a Release method. Here are those methods:
CAudioScriptPtr::CAudioScriptPtr(CAudioScript* p) : CRefCountPtr<CAudioScript>(p)
{
stringstream str;
if (p) {
str << "CAudioScriptPtr::CAudioScriptPtr: constructed for pointer "
<< p << " name: " << p->GetName() << " (count = "
<< itsCounter->count << ")..." << endl;
OutputDebugString(str.str().c_str());
}
}
void CAudioScriptPtr::Release()
{
stringstream str;
if (itsCounter) {
str << "CAudioScriptPtr::Release(): pointer " << itsCounter->ptr
<< " name: " << itsCounter->ptr->GetName()
<< " (count = " << itsCounter->count << ")..." << endl;
OutputDebugString(str.str().c_str());
}
}
Neither of these methods do anything critical—they just talk a lot. The constructor echoes a status line to the DevStudio trace output, so you can see exactly when smart pointers are constructed, and the Release method echoes a line so you can make sure things are freed correctly. Notice that these routines output the name of the CAudioScript class, fetched via a call to the GetName method that you'll create later. Note that Release doesn't actually do anything—the real pointer deletion takes place inside CRefCountPtr.The CAudioScript smart pointer is just like the CSound smart pointer, only with different trace output and a different template parameter to CRefCountPtr. The CAudioScriptPtr displays the name of the audio script it's working on, so you could say that CAudioScriptPtr is slightly smarter than CSoundPtr, though since both know exactly the right time to delete their pointers, they're both pretty smart.
CAudioScript
With the new CAudioScriptPtr smart pointer class created, you can now create the CAudioScript class itself. Here's the declaration:
class CAudioScript
{
public:
friend class CAudioManager;
CAudioScript(CAudioManager *mgr);
virtual ~CAudioScript();
void SetVariable(std::string varname, int value);
int GetVariable(std::string varname);
void CallRoutine(std::string procname);
std::vector<std::string> EnumVariables();
std::vector<std::string> EnumRoutines();
std::string GetName() { return(m_Name); }
std::string AssembleErrorInfoString(std::string header,
DMUS_SCRIPT_ERRORINFO &errInfo);
protected:
std::string m_Name;
void Init(std::string name);
CAudioManager *m_Manager;
IDirectMusicScript8 *m_Script;
unsigned char *m_OriginalData;
};
There should be a lot of things here that you recognize, and a few things you don't. Starting with the familiar, notice that CAudioScript has an m_OriginalData pointer, just like CSound does. This m_OriginalData pointer is set by the audio manager when an audio script is created from raw memory—just like the CSound derivatives, the CAudioScript class remembers the memory it was born from, and it frees that memory when it's destroyed. Notice also that just like CSound, CAudioScript is a friend of CAudioManager (so that CAudioManager can access CAudioScript's private and protected members directly when it's loading a new audio script), and that a new CAudioScript class can only be constructed by providing a CAudioManager pointer (so that every CAudioScript has a pointer to a CAudioManager instance, and therefore a DirectMusic performance interface).And now, here are the new things. At the heart of CAudioScript is m_Script, the IDirectMusicScript8 interface, provided by DirectX Audio. You can also see that the audio script has an m_Name member, accessed through the GetName and SetName methods. This is just to make things simpler for the game programmer—the name of the audio script will be echoed to the trace output by the CAudioScript smart pointer on creation and release. You'll see how the code decides on a name in the next section.CAudioScript also has a private Init function, which takes care of performing one-time initialization on the script. This Init function is called only by CAudioManager (a friend, able to call protected functions) when a new script is loaded. Finally, there are methods to get and set variables, call a routine, enumerate the available variables and routines, and assemble error strings, all of which you'll learn about in the following sections.
Initializing and Loading a Script
Before you can do anything with an audio script, you must load and initialize it. This means you need three new methods inside CAudioManager: one method to load from disk, one to load from resources, and one to load from memory. Here's a look at the disk-loading method:
CAudioScriptPtr CAudioManager::LoadScript(std::string filename,
std::string name)
{
CAudioScript *script = new CAudioScript(this);
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_DirectMusicScript,
IID_IDirectMusicScript8,
widefilename,
(void**) &script->m_Script);
ThrowIfFailed(hr, "LoadObjectFromFile failed.");
if (!name.length()) { name = filename; }
script->Init(name);
return(CAudioScriptPtr(script));
}
By now, you should be familiar and comfortable with all of the actions performed by this LoadScript method. It creates a new CAudioScript, converts the given filename into wide-string, tells DirectMusic to load the file (specifying CLSID_DirectMusicScript and IID_IDirectMusicScript8 for the class and interface, respectively), and stuffs the interface DirectMusic provides into the script object's m_Script variable. Once all that's complete, it sets the script object's name and calls its Init method. When Init is complete, it returns a new smart pointer to the new script.The only thing mysterious here is the Init method of CAudioScript. Here's what that looks like:
void CAudioScript::Init(string name)
{
m_Name = name;
DMUS_SCRIPT_ERRORINFO errInfo;
HRESULT hr;
errInfo.dwSize = sizeof(DMUS_SCRIPT_ERRORINFO);
hr = m_Script->Init( m_Manager->GetPerformance(), &errInfo);
if (FAILED(hr)) {
ThrowIfFailed(hr,
AssembleErrorInfoString("m_Script->Init() failed!", errInfo));
}
}
Init is short and sweet. All it's really doing is calling the Init method of the audio script interface. This is a requirement by DirectX Audio; before you do anything with a script interface, you must initialize it by telling it what performance it's tied to.Notice the DMUS_SCRIPT_ERRORINFO object, errInfo. The address of this structure is passed as the second argument to IDirectMusicScript8::Init, and the code gives the structure to AssembleErrorInfoString when something goes wrong.The DMUS_SCRIPT_ERRORINFO object is cool. When you Init an IDirectMusicScript8 interface, you're actually compiling the AudioVB code, so if there are any errors in the code, initialization will fail, and errInfo will be filled with all sorts of useful information. You'll get the file and line number of the error, along with a description of the error and the component that generated the error (that is, the name of the DirectX component that has a problem with your script).The AssembleErrorInfoString uses the information in the DMUS_SCRIPT_ERRORINFO object it's given to create a detailed error string. This error string is then thrown via the ThrowIfFailed macro, and shown in a dialog similar to that in Figure 12.6.

Figure 12.6: The error dialog generated by AssembleErrorInfoString.
Here's what AssembleErrorInfoString looks like:
string CAudioScript::AssembleErrorInfoString(string header, DMUS_SCRIPT_ERRORINFO
&errInfo)
{
stringstream str;
str << header << endl;
// put error info into string!
char file[DMUS_MAX_FILENAME];
char desc[DMUS_MAX_FILENAME];
char component[DMUS_MAX_FILENAME];
DXUtil_ConvertWideStringToGeneric(file,
errInfo.wszSourceFile, sizeof(file));
DXUtil_ConvertWideStringToGeneric(desc,
errInfo.wszDescription, sizeof(desc));
DXUtil_ConvertWideStringToGeneric(component,
errInfo.wszSourceComponent, sizeof(component));
str << "There was an error in script file \" << GetName()
<< "\"." << endl << endl;
str << "File: " << file << ", line " << errInfo.ulLineNumber
<< ": " << desc << endl
<< endl; str << "Error reported by: " << component << endl;
return(str.str());
}
The only irritating thing about the DMUS_SCRIPT_ERRORINFO object is that all of its string members are wide-character, so you have to convert them to normal ASCII strings (usually via the DXUtil_ConvertWideStringToGeneric function). Once you do that, you have detailed information about the error, which you can use as you please—the AssembleErrorInfoString function outputs everything into one big STL string, and returns that.
Enumerating Variables and Routines
Once you've got an audio script loaded and initialized, you can peek inside it using methods that enumerate its subroutines and variables. Here's how CAudioScript's EnumRoutines method gets a list of all the routines in the script:
vector<string> CAudioScript::EnumRoutines()
{
vector<string> routines;
int count = 0;
HRESULT hr;
bool done=false;
while (!done) {
WCHAR widename[MAX_PATH];
char name[MAX_PATH];
hr = m_Script->EnumRoutine(count, widename);
ThrowIfFailed(hr, " EnumRoutine failed.");
if (hr == S_FALSE) done=true;
else {
DXUtil_ConvertWideStringToGeneric(name, widename, sizeof(name));
routines.push_back(name);
count++;
}
}
return(routines);
}
This code loops, repeatedly calling the EnumRoutine method of IDirectMusicScript8. That method takes two parameters—the index of the routine you're interested in, and a pointer to a wide-character array that will be filled in with its name. If the given index is too high, it returns S_FALSE.This makes getting a list of routines very easy. The code starts with index zero, calls EnumRoutine, and looks at the return value. If it's S_FALSE, it stops the loop; otherwise, it converts the name it got back into a normal string, and pushes it onto the vector.The implementation of CAudioScript::EnumVariables works the same way, only instead of calling EnumRoutine, it calls EnumVariable.
Setting Variable Values
So, you've loaded a script, initialized it, and maybe you've looked inside it by enumerating its variables and routines. Now you want to communicate with it by setting some of its variables, in preparation for calling one of its routines. Look no further than the CAudioScript::SetVariable routine:
void CAudioScript::SetVariable(string varname, int value)
{
DMUS_SCRIPT_ERRORINFO errInfo;
WCHAR widevarname[MAX_PATH];
DXUtil_ConvertGenericStringToWide(widevarname, varname.c_str());
HRESULT hr = m_Script->SetVariableNumber(widevarname,
value, &errInfo);
if (FAILED(hr)) {
ThrowIfFailed(hr,
AssembleErrorInfoString("SetVariableNumber failed!", errInfo));
}
}
Setting an integer number is as easy as calling the SetVariableNumber method of IDirectMusicScript8. Give this method the name of the variable you want to set (as always, in wide-character format!), along with a value, and DirectX Audio will set that variable if it exists (for example, if the audio script has dim'd it), or it will fill up a DMUS_SCRIPT_ERRORINFO structure if something goes wrong.
Tip | You can get and set variables as string values, using a process similar to that of integer values. Check out the DirectX Audio documentation for the SetVariableVariant method. |
Getting Variable Values
Retrieving a variable's current value is just as easy:
int CAudioScript::GetVariable(string varname)
{
DMUS_SCRIPT_ERRORINFO errInfo;
WCHAR widevarname[MAX_PATH];
long value=0;
DXUtil_ConvertGenericStringToWide(widevarname, varname.c_str());
HRESULT hr = m_Script->GetVariableNumber(widevarname,
&value, &errInfo);
if (FAILED(hr)) {
ThrowIfFailed(hr,
AssembleErrorInfoString("GetVariableNumber failed!", errInfo));
}
return(value);
}
The GetVariableNumber script puts the value of the given wide-character variable name into a long pointer, or it fails and fills in a DMUS_SCRIPT_ERRORINFO structure if something goes wrong.
Calling a Routine
Once you've set up your variable values, calling a routine is as easy as calling the CallRoutine method of IDirectMusicScript8, as shown by the following code:
void CAudioScript::CallRoutine(string procname)
{
DMUS_SCRIPT_ERRORINFO errInfo;
WCHAR wideprocname[MAX_PATH];
long value=0;
DXUtil_ConvertGenericStringToWide(wideprocname, procname.c_str());
HRESULT hr = m_Script->CallRoutine(wideprocname, &errInfo);
if (FAILED(hr)) {
ThrowIfFailed(hr,
AssembleErrorInfoString("CallRoutine failed!", errInfo));
}
}
Again, aside from the now-standard wrapper for dealing with the errInfo structure, there isn't much to this. Convert the procedure name you want to call into a wide-character string, call CallRoutine, and DirectX does the rest!To wrap up, here are a few final notes about audio script subroutines. First, they execute synchronously—that is, CallRoutine doesn't return until it hits the end sub line of the routine you called, so your game code can fall victim to infinite loops and hanging caused by a buggy script.Second, realize that you can't pass arguments to audio script routines; everything must be in the form of global variables that the subroutine can access. This underscores the need for a consistent naming convention; consider making fake subroutine arguments by prefixing the name of the subroutine onto all arguments used by that subroutine.Finally, be aware that audio scripts can be called by DirectMusic itself; for example, you can embed calls to audio script subroutines inside other DirectMusic objects. This isn't necessarily something to fear, just something for you and your sound designer to be aware of, as it can be useful in certain situations.
The Audio Script in the Game
You now have a complete CAudioScript object, just dying to be used in an actual program. Here are some quick notes on how to use CAudioScript to accomplish the cat meow scenario discussed at the beginning of the chapter.The complete code for that example is contained in Ch12p1_AudioScripts. Obviously, it's a far cry from a real game example, but it shows you everything you need to implement audio scripting in your own game.
Audio Scripting Wrap-Up
You've been given a brief overview of audio scripting, and if you've never dealt with integrating a scripting language into a game, you've probably learned many new things. If you'd like to learn more about audio scripting, consult the DirectX Audio documentation, at DirectX Audio\Understanding DirectX Audio\Audio Scripts, and DirectX Audio\Using DirectX Audio\Using Audio Scripts. Also, check out the links on your CD.