Adding Scripting to Jones


As it turns out, adding scripting support to Jones is not hard at all. We would like to be able to:

  • Load a script

  • Display all script routines

  • Call routines

  • Display all script variables

  • Set variable values

Jones' scripting solution adds a series of list boxes on the left side of the Jones window, as seen in Figure 12-2. The top box lists all currently loaded scripts. Underneath it, two list boxes display all routines and variables for the currently selected script. To call a routine, simply double-click on it or, alternately, select it and press the Call button. To view a variable, click on it once and view it in the text box. To change it, edit its value in the text box.

click to expand
Figure 12-2: Jones with scripting support.

Jones Data Structures

We add a new class, CScript, to our audio library. CScript keeps track of one instance of a loaded script via its IDirectMusicScript interface. I considered creating data structures to manage the lists of routines and variables, but the enumeration routines provided by IDirectMusicScript are so straightforward, it seemed silly to do so.

We start with the relatively simple CScript class. It tracks the name and script interface and provides access methods to both. The biggest item is the initialization method.

 class CScript : public CMyNode { public:     CScript();     ~CScript();     CScript*   GetNext() { return (CScript*)CMyNode::GetNext();}     IDirectMusicScript *GetScript() {return m_pScript; };     void Init(IDirectMusicScript *pScript,         IDirectMusicPerformance8 *pPerformance);     char *GetName() { return m_szName; }; private:     IDirectMusicScript * m_pScript;     char m_szName[DMUS_MAX_NAME];  // Name, for display }; 

CScript is managed in a link list by CScriptList. We add one instance of CScriptList to CAudio to manage all of the scripts, and we add a method for loading scripts:

   CScript *LoadScript(WCHAR *pwzFileName);  // Load a script from file.   CScriptList m_ScriptList;                 // List of loaded scripts. 

That is it.

Loading the Script

To load a script, click on the Open button. This opens the standard file dialog and prompts you to load a script file with the extension .spt.

Select a script file (I would recommend SimpleScript.spt in the Media directory) and click on Open. The script name is placed in the script list, and its routines and variables appear in the two lists below.

Let's look at LoadScript(). It uses the Loader to read a script from the passed file path. Once LoadScript() successfully reads the script file, it calls the CScript's Init() method, which prepares the script for running.

 CScript *CAudio::LoadScript(WCHAR *pwzFileName) {   WCHAR wzSearchDirectory[DMUS_MAX_FILENAME];   wcscpy(wzSearchDirectory,pwzFileName);   WCHAR *pwzEnd = wcsrchr(wzSearchDirectory,'\\');   if (pwzEnd)   {       // If we have a directory path, use it to set       // up the search directory in the Loader.       // The Loader will look here for linked files,       // including Segments, Styles, and DLS instruments.       *pwzEnd = 0;       m_pLoader->SetSearchDirectory(GUID_DirectMusicAllTypes,           wzSearchDirectory,FALSE);   }   CScript *pScript = NULL;   IDirectMusicScript8 *pIScript;   // Now, load the script.   if (SUCCEEDED(m_pLoader->LoadObjectFromFile(     CLSID_DirectMusicScript,      // Class ID for script.     IID_IDirectMusicScript8,      // Interface ID for script.     pwzFileName,                  // File path.     (void **) &pIScript)))        // Returned IDirectMusicScript.   {     // Create a CScript object to manage it.     pScript = new CScript;     if (pScript)     {       // Initialize and add to list.       pScript->Init(pIScript,m_pPerformance);       m_ScriptList.AddTail(pScript);     }     pIScript->Release();   }   return pScript; } 

CScript::Init() is similarly simple. It gets the script ready to run by calling the script's IDirectMusicScript::Init() routine and then retrieving the script's name so it can be displayed.

 void CScript::Init(IDirectMusicScript *pScript,                    IDirectMusicPerformance8 *pPerformance) {   m_pScript = pScript;   pScript->AddRef();   // Initialize the script for this performance.   // In addition to providing a global performance pointer   // for the scripting, this automatically downloads   // all waves and DLS instruments, if this option was set   // in the script at authoring time (which is usually the case).   pScript->Init(pPerformance,NULL);   // Get the object descriptor from the script. This includes the name.   IDirectMusicObject *pIObject = NULL;   pScript->QueryInterface(IID_IDirectMusicObject,(void **)&pIObject);   if (pIObject)   {     DMUS_OBJECTDESC Desc;     Desc.dwSize = sizeof(Desc);     pIObject->GetDescriptor(&Desc);     pIObject->Release();     m_szName[0] = 0;     if (Desc.dwValidData & DMUS_OBJ_NAME)     {         wcstombs(m_szName, Desc.wszName, DMUS_MAX_NAME);     }     else if (Desc.dwValidData & DMUS_OBJ_FILENAME)     {         wcstombs(m_szName, Desc.wszFileName, DMUS_MAX_NAME);         // Get rid of any file path.         char *pszName = strrchr(m_szName,'\\');         if (pszName) strcpy(m_szName,++pszName);     }     else     {         strcpy(m_szName,"Script (no name)");     }   } } 

Enumerating and Calling Routines

Once the script is loaded, Jones displays the list of available script routines in the Routines list box. To run a routine, double-click on its name in the Routines list.

To create that list of routines, Jones calls the IDirectMusicScript:: EnumRoutine() method. I thought of wrapping this somehow in CScript for completeness, but that seemed silly. So, here's the code inside Jones' UI that enumerates the routines in the process of creating the list box. Notice that EnumRoutine() signals the end of list with S_FALSE, not an error condition.

    // Enumerate through the script's routines.    DWORD dwIndex;    HRESULT hr = S_OK;    for (dwIndex = 0; hr == S_OK; dwIndex++)    {        // Names are returned in Unicode.        WCHAR wzName[DMUS_MAX_NAME];        hr = pScript->GetScript()->EnumRoutine(dwIndex,wzName);        if (hr == S_OK)        {            // Convert from Unicode to ASCII and add to list box.            char szName[DMUS_MAX_NAME];            wcstombs(szName, wzName, DMUS_MAX_NAME);            m_ctScriptRoutineList.AddString(szName);        }    } 

To play the routine, Jones retrieves the routine name from the routine list and uses it to call CallRoutine().

    // Convert routine name to Unicode.    WCHAR wszName[MAX_PATH];    mbstowcs(wszName,szName,MAX_PATH);    // Use the Unicode name to call the routine.    pScript->GetScript()->CallRoutine(wszName,NULL); 

Enumerating and Accessing Variables

The list of variables is displayed below the routines.

Click on a variable to show its value in the edit box. Type over the variable value with a new value and Jones automatically sends the new value to the script.

Scripting supports three ways to access variables: objects, variants, and numbers. However, to keep things simple in Jones, we are only supporting numbers. So, when we enumerate through the variables using IDirectMusicScript::EnumVariable(), we need to make sure that the variable is valid as a number and we want to retrieve the current value, which we can store in our list. We do both with a call to IDirectMusicScript::GetVariableNumber(). If the call succeeds, we can display the variable.

    DWORD dwIndex;    HRESULT hr = S_OK;    for (dwIndex = 0; hr == S_OK; dwIndex++)    {       WCHAR wzName[DMUS_MAX_NAME];       // Enumerate through all variables       hr = pScript->GetScript()->EnumVariable(dwIndex,wzName);       if (hr == S_OK)       {          long lData = 0;          // But verify that each variable is capable of providing a number.          // If not, ignore it.          hr = pScript->GetScript()->GetVariableNumber(wzName,&lData,NULL);          if (SUCCEEDED(hr))          {             // Success. Convert to ASCII and place in the list box.             char szName[DMUS_MAX_NAME] = "";             wcstombs(szName, wzName, DMUS_MAX_NAME);             int nIndex = m_ctScriptVariableList.AddString(szName);             m_ctScriptVariableList.SetItemData(nIndex,(DWORD)lData);          }       }    } 

Next, we need to be able to edit the variable. When a variable is selected in the list, Jones pulls the variable data from the list (previously retrieved with GetVariableNumber()) and places it in the edit box. Then, in response to user edit, it sets the new value.

    // Get the name of the variable.    char szName[MAX_PATH];    m_ctScriptVariableList.GetText(nSelect,szName);    // Convert the name to Unicode.    WCHAR wszName[MAX_PATH];    mbstowcs(wszName,szName,MAX_PATH);    // Use the name to set the variable to the new value.    pScript->GetScript()->SetVariableNumber(wszName,m_lScriptVariable,NULL); 




DirectX 9 Audio Exposed(c) Interactive Audio Development
DirectX 9 Audio Exposed: Interactive Audio Development
ISBN: 1556222882
EAN: 2147483647
Year: 2006
Pages: 170

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net