A lesser-known feature of ReaWwise is that it exposes raw WAAPI functions to REAPER, which you can use in your own custom ReaScripts. In this blog article, we’ll look at implementing some basic Wwise-related functionality using WAAPI, all in the comfort of the ReaScript Development Environment.
Prerequisites
WAAPI & Lua
Before continuing, I recommend that you have a basic understanding of WAAPI, Lua, and ReaScript. Some resources to learn those are here:
ReaWwise
To run WAAPI commands from within a ReaScript, you must install ReaWwise.
Getting Started
The ReaScript Development Environment
To get to the ReaScript Development Environment, open REAPER. In the REAPER menu, select Actions > Show action list. In the Actions dialog, in the lower-right corner, click New action > New ReaScript. At this point, you will be prompted to save a file. Name your ReaScript file and click Save. After saving, the ReaScript Development Environment opens. This is where we will write our code.
Hello Wworld
-- Waapi Hello World.lua
if(reaper.AK_Waapi_Connect("127.0.0.1", 8080)) then
reaper.ShowConsoleMsg("Successfully connected to Wwise!")
reaper.AK_Waapi_Disconnect()
end
In the preceding code snippet, we first make a call to AK_Waapi_Connect. This function takes in the IP and PORT on which WAAPI should communicate with Wwise. The function returns a boolean value: true if the connection was successful and false if otherwise. At the end of the if statement, we make sure to terminate the connection by calling AK_Waapi_Disconnect. If the script runs successfully, the REAPER console output window pops up a message like so:
Basic Example (Get Selected Objects)
In this next example, we look at querying the currently selected objects in Wwise.
-- Get Selected Objects.lua
if(reaper.AK_Waapi_Connect("127.0.0.1", 8080)) then
local fieldsToReturn = reaper.AK_AkJson_Array()
reaper.AK_AkJson_Array_Add(fieldsToReturn, reaper.AK_AkVariant_String("path"))
local options = reaper.AK_AkJson_Map()
reaper.AK_AkJson_Map_Set(options, "return", fieldsToReturn)
local result = reaper.AK_Waapi_Call("ak.wwise.ui.getSelectedObjects",
reaper.AK_AkJson_Map(), options)
local status = reaper.AK_AkJson_GetStatus(result)
if(status) then
local objects = reaper.AK_AkJson_Map_Get(result, "objects")
local numObjects = reaper.AK_AkJson_Array_Size(objects)
for i=0, numObjects - 1 do
local item = reaper.AK_AkJson_Array_Get(objects, i)
local path = reaper.AK_AkJson_Map_Get(item, "path")
local pathStr = reaper.AK_AkVariant_GetString(path)
reaper.ShowConsoleMsg(pathStr .. "\n")
end
end
reaper.AK_AkJson_ClearAll()
reaper.AK_Waapi_Disconnect()
end
AkJson
To facilitate the creation of JSON objects, ReaWwise exports various helper functions. These allow ReaScripts to dynamically create JSON objects based on the scripter's needs.
Calls to WAAPI require three elements: arguments, options, and a command string. For this example, the command string is ak.wwise.ui.getSelectedObjects. An exhaustive list of WAAPI commands can be found in the WAAPI Reference.
For this specific command, WAAPI expects an empty map as the arguments. All we need to do then is set up the options map.
The options map we want to create will look like this:
{
"return": [
"path"
]
}
Due to the fact that only value types and pointers can be passed between REAPER’s Lua context and the ReaWwise back end, the options map needs to be created in multiple steps.
We start by creating the fieldsToReturn array:
local fieldsToReturn = reaper.AK_AkJson_Array()
What gets returned in the fieldsToReturn variable is a pointer to an array in the ReaWwise back end. With this pointer, we can operate on the array.
We then add the "path" string as an element to the fieldsToReturn:
reaper.AK_AkJson_Array_Add(fieldsToReturn, reaper
.AK_AkVariant_String("path"))
Finally, we create the options map and add the fieldsToReturn array as a value with key "return":
local options = reaper.AK_AkJson_Map()
reaper.AK_AkJson_Map_Set(options, "return", fieldsToReturn)
Call to WAAPI
Once we’ve created all the required parameters, we use them as inputs in the call to WAAPI:
local result = reaper.AK_Waapi_Call("ak.wwise.ui.getSelectedObjects",
reaper.AK_AkJson_Map(), options)
We can then inspect the result to see if the call to WAAPI succeeded. Remember, complex data structures cannot be passed directly from the ReaWwise back end to the Lua context. The result variable is a pointer.
We can query the status:
local status = reaper.AK_AkJson_GetStatus(result)
If the status is true, we can proceed with querying the actual data returned in the call to WAAPI:
local objects = reaper.AK_AkJson_Map_Get(result, "objects")
local numObjects = reaper.AK_AkJson_Array_Size(objects)
Then, we iterate over numObjects and extract the data we need:
for i=0, numObjects - 1 do
...
end
Each object is represented as a map. To access a specific attribute of the object, we use the helper functions offered by the ReaWwise back end.
To get a single object from the objects array, we use the objects pointer and an index value:
local item = reaper.AK_AkJson_Array_Get(objects, i)
The variable item is a pointer to a map object that represents a single object in the objects array. We can then extract the path variable:
local path = reaper.AK_AkJson_Map_Get(item, "path")
Map and array values are stored internally as variant objects. To extract the final value, we need to know what data the value represents. In our case, we know we should be getting a string. We use the following helper function to get a string from the variant data type:
local pathStr = reaper.AK_AkVariant_GetString(path)
The variable pathStr is a regular Lua string that could be used throughout the rest of the script. The script simply outputs pathStr to the REAPER console.
reaper.ShowConsoleMsg(pathStr .. "\n")
Since the ReaWwise back end needs to keep track of reference type objects used throughout the Lua code, it’s important to clear them when they are not needed anymore:
reaper.AK_AkJson_ClearAll()
Advanced Example (Import)
In the next example, we will do something a little more complex. We will be transferring audio files into Wwise from the REAPER render directory. Each audio file will be imported as a Sound SFX under the import destination that has been hardcoded in the script. The underlying WAAPI command that will be used is ak.wwise.core.audio.import.
For this example, I won’t explain what each line of code does since most of the concepts regarding the API were explained in the previous example. I will, however, give a general description of what each section of the code is doing.
Lines 3-13: We connect to WAAPI, set up the importDestination, and then deduce the render directory. In this script, we assume that the render directory is the same as the parent directory of the project file.
Lines 15-41: We build all the AkJson structures to be passed as inputs to the AK_Waapi_Call function.
Lines 44-70: We iterate through the render directory and add any WAV file encountered to the list of audio files to be imported.
Lines 74-101: We execute the AK_WAAPI_Call function and display the results in the REAPER console. These lines also contain logic that extracts error information from the result in the case that an error is encountered.
Conclusion
In this article, we got to see several examples of WAAPI being used directly in a Lua ReaScript. From a basic connection to Wwise to querying Wwise, and then doing something quite complex such as importing audio files. We hope that this API will be beneficial to you and your workflows. We are excited to see how creative you get with WAAPI in your ReaScripts.
Comments