Blog homepage

Introducing Wwise Authoring Query Language

Audio Programming / Wwise Tips & Tools

“Wwise is a spreadsheet”. This is something we hear often as members of the user experience team. Truth is, while Wwise is a sound design tool, under the hood, a Wwise project remains data. A lot of data. Today’s reality is that games are huge and it is better to be in control of your data if you want to avoid problems. 

Fortunately, Wwise offers tools to manipulate large amounts of data, such as the List View, the Multi Editor, the Batch Rename and the Query Editor.

WAQL_List-1

There are also many techniques and good practices to improve the organization inside a Wwise project to facilitate handling large amounts of data. For example, creating Work Units, using a naming convention, importing audio files with tab delimited files, using custom properties, leveraging the batch renamer, assigning colors, etc.

You also have the Search and the Query Editor  to help you find your data. But these tools have some limitations. What if you want to find out which random containers in your project have a looping sound? What are the audio sources being referred to by an Event?

A couple of years ago, we introduced WAAPI, a programming API that empowers programmers to automate, manipulate and query the Wwise project. But WAAPI requires you to use a programming language. This power remains mostly in the hands of programmers, hardly accessible to the rest of Wwise users. Additionally, having a WAAPI script up and running requires some setup time.

Here is a WAAPI example, written in Python, printing all the converted files being referred to by an Event. This is not exactly easy to read.

from waapi import WaapiClient
import pprint

# Connect (default URL)
client = WaapiClient() 

# Return all targets
args = {
    "from": {"path": ['\\Events\\Default Work Unit\\Play']},
    "transform": [
        {"select": ['children']}
    ]
}

options = {
    "return": ['@Target']
}
result = client.call("ak.wwise.core.object.get", args, options=options)
pprint.pprint(result)

# Return all wem files from targets
args = {
    "from": {"id": list(map(lambda x: x['@Target']['id'], result['return']))},
    "transform": [
        {"select": ['descendants']},
        {"where": ['type:isIn', ['AudioFileSource']]}
    ]
}

options = {
    "return": ['name', 'id','sound:convertedWemFilePath']
}
result = client.call("ak.wwise.core.object.get", args, options=options)
pprint.pprint(result)

# Disconnect
client.disconnect()

Introducing WAQL

Wwise 2021.1 introduces WAQL. It stands for Wwise Authoring Query Language and it’s pronounced wah-kel. If you are a JIRA user, you might be familiar with the JIRA Query Language (JQL) as a way to query the JIRA database. Programmers might be familiar with C# LINQ, or SQL. WAQL is somewhat similar to these languages. It allows for querying the Wwise project through its data model. Essentially, queries the Wwise objects and their properties.

WAQL is the next step in our plan to bring more control over the data within your project. It’s still a new language to learn, but the barrier is much lower than learning a complete programming language like Python, Javascript, or C#. And more importantly, it is accessible out-of-the-box from Wwise Authoring. The goal of this article is to help you understand the basics of WAQL. We actually recommend you try it in your Wwise project as you read the article. This article is a tutorial and an introduction to WAQL.

OK, let’s start with something simple. Let’s find all the objects in your project where the volume is less than zero. To do that, open the List View, and type: $ where volume < 0.

WAQL_list_view

Wait! Let’s try to understand what is going on. First, when typing in the List View search bar, it normally executes a text search across the entire project, but now since we started by typing a $, it tells Wwise that we are about to start a WAQL query instead of a simple search.

Then, the where keyword indicates that we are about to write a conditional statement. By default, when starting a query with a where, the condition will be executed on all objects within the project. Finally, we write the conditional statement. In this case, we ask to compare the volume, a property on a Wwise object, with a number: zero. The condition will filter out objects for which the volume is not less than zero.

Wwise Objects and Properties

This brings us to another topic: Wwise objects and properties. Wwise objects are essentially all the entries found in the Project Explorer, plus some other objects that are hidden or visible inside the different systems. Properties are defined inside the objects and they store values.

There are many properties in Wwise. For example, these are the properties found in the General Settings tab of the Property Editor for a Sound object:

WAQL_Markers

Properties can store any one of the following:

  • A value (integer number, real number, boolean, string). Ex: Volume
  • A reference to another object in the project. Ex: OutputBus. Effect0
  • A local object, known as ‘Custom’. Ex: Effect0

A complete list of all properties for all Wwise objects can be found here.

Additionally, there are core properties that can be found on most objects. For instance, the ‘name’, ‘notes’, ‘path’ and ‘id’ are core properties accessible on all Wwise objects, while the ‘volume’ and ‘outputbus’ are properties found on certain types of objects. The core properties are sometimes calculated, and sometimes stored with the object. In all cases, property names in WAQL are case-insensitive. 

For more information about core properties, refer here

Getting Started with Conditions

As we saw earlier, the where statement defines a condition. The condition will be evaluated on every object given to the where statement. Conditions are defined by a boolean expression; a statement that can be either true or false.

Here are a couple of examples:

  • $ where pitch = 1200
  • $ where volume > -10 and volume < 0
  • $ where IsLoopingEnabled or IsStreamingEnabled 
  • $ where (volume >= 0 and (lowpass > 0 or highpass > 0))

Now try to experiment with more scenarios using the where conditions.

We can also search and compare text with WAQL using one of the three string comparison operators:

  • $ where name = "Hello"
    The equal symbol is used to perform a case-insensitive comparison of the whole string with another string, and return true when the strings are exactly the same.
  • $ where notes : "hell"
    The colon symbol is used to find the beginning of a word inside a string. It is case insensitive. It returns true if the word is found.
  • $ where outputbus = /^Music\d+$/
    The equal operator can also be used with a regular expression (ECMAScript style).

Chaining Objects and Properties

There are two types of properties on Wwise objects:

Properties returning a value:

  • volume, pitch, lowpass, etc: Commonly associated with a slider in Wwise, they store a numerical value. These vary depending on the object type. For example, Sound and Random Containers do not have the same set of properties.
  • name, notes, id, path: Core elements of the Wwise object system, they often store a string value. These are found on any Wwise object.

Properties returning another Wwise object (also known as references):

  • outputbus, target, userauxsend0, effect0: These point to another object and they vary depending on the object type. For example Events and Sounds do not have the same properties.
  • parent: pointer to the parent object.

It's possible to combine objects and values using the dot symbol. For example:

  • $ where parent.name = "Music"
    The parent’s name is “Music”.
  • $ where parent.parent.name = "Music"
    The grandparent’s name is “Music”.
  • $ where parent.volume < 0
    The parent’s volume is less than zero.
  • $ where outputbus.parent.name = "Master Audio Bus"
    The output bus’s parent name is “Master Audio Bus”.
  • $ where effect0.pluginname = "Wwise Compressor"
    The plug-in name for the effect at slot 0 is “Wwise Compressor”.

Starting From a Different Source

So far, we have been filtering objects from the entire project. However, in some instances we may wish to focus our search on a narrower set of objects. WAQL also allows you to set the query source as:

  • All objects of the specified types
  • One or more specific objects in the project
  • The result of a legacy query object (Query Editor)
  • The result of a text search query

Here are some examples to try:

  • $ from type sound
  • $ from type audiofilesource
  • $ from type randomsequencecontainer, switchcontainer, blendcontainer

Specifying the type directly in the from statement is a very efficient way to narrow the search. WAQL will restrict the iteration to the specified source. The last query could also be written with a filter search using the where keyword. However, the filtering would have to go through all project objects, including Events, busses, sounds, etc. The execution time will be slower:

  • $ where type = "randomsequencecontainer" or type = "switchcontainer" or type = "blendcontainer"

Try with other object types. Refer here.

It can also be very convenient to start from a single object:

  • $ from object "\Actor-Mixer Hierarchy\Default Work Unit"
  • $ from object "{1514A4D8-1DA6-412A-A17E-75CA0C2149F3}"
  • $ from object "Event:Play_Footstep_01"7

The previous query can also be written with the shorter form, by omitting the from object:

  • $ "\Actor-Mixer Hierarchy\Default Work Unit"
  • $ "{1514A4D8-1DA6-412A-A17E-75CA0C2149F3}"
  • $ "Event:Play_Footstep_01"

And we can also specify many objects:

  • $ "\Actor-Mixer Hierarchy",  "\Interactive Music Hierarchy"
  • $ "{DE2B0843-131F-4E52-BC71-23C43A5324AB}", "{1514A4D8-1DA6-412A-A17E-75CA0C2149F3}"
  • $ "Event:Play_Footstep_01", "Event:Play_Footstep_02", "Event:Play_Footstep_03"

Here are other examples of queries with different sources as the starting point:

  • $ from search "foot walk"
  • $ from search "hello" where type = "sound"
  • $ from query "\Queries\Factory Queries\Audio Source\Audio Source - Format = Vorbis"

Selecting Objects

So far, we learned how to specify the source of a WAQL query with the from keyword, and we learned how to filter a WAQL query with the where keyword. Now, let’s see how to further transform the results with the select keyword.

Selecting objects allows you to obtain other objects from the initial sequence of objects. Always keep in mind that each part of a WAQL query feeds the next one.

Type the following query in the List View:

  • $ from object "\Actor-Mixer Hierarchy\Default Work Unit" select children

This takes the Default Work Unit and returns all direct children of it. We can find two parts in this query: the from statement and the select statement. The from statement defines the beginning of the query, or the source. The select statement is executed on every object coming from the source.

Here are other queries selecting different elements in the hierarchy of objects:

  • $ "\Actor-Mixer Hierarchy\Default Work Unit" select descendants
    Return all children recursively.
  • $ "\Actor-Mixer Hierarchy\Default Work Unit" select parent
    Return the direct parent.
    $ "\Actor-Mixer Hierarchy\Default Work Unit" select parent.parent
    Return the direct grandparent.
  • $ "\Actor-Mixer Hierarchy\Default Work Unit" select ancestors
    Return all parents recursively.
  • $ "\Actor-Mixer Hierarchy\Default Work Unit" select parent, children
    Return the parent combined with the children.
  • $ "\Actor-Mixer Hierarchy\Default Work Unit" select this, parent, children
    Return the Default Work Unit itself, its parent combined with its children.
  • $ from type sound select parent
    Return all the parents for all the sounds.

Not only can you navigate the children and parents of the hierarchy with the select keyword, you can also select objects from properties:

  • $ from type sound select effect0
    Return all effects at index 0 found for all sound objects.
  • $ from type sound select effect0, effect1, effect2, effect3
    Return all effects found for all sound objects.
  • $ from type sound select outputbus
    Return all busses found at the Output Bus for all sound objects.
  • $ from type sound select outputbus.parent
    Return the parents of the busses found at the Output Bus for all sound objects.

Additional exercises:

  • Try adding a where statement after a select statement
  • Try inserting a where statement before the select statement
  • Try select referencesto

Disecting a Complex Query

Let’s take the following query, which enumerates all Sound objects that are referenced by an Event. The query has five statements chained together:

  • $ from type event select children select target select this, descendants where type = "sound"

Here is each part of the query:

  • $ from type event
    Start by enumerating all Events from the project
  • $ from type event select children
    Then obtain the Event actions (direct children) from the Event objects.
  • $ from type event select children select target
    Then obtain the targets (reference named target) for each action.
  • $ from type event select children select target select this, descendants
    Then obtain the targets themselves (using the keyword this) and their descendants.
  • $ from type event select children select target select this, descendants where type = "sound"
    Finally, only keep only the sound objects.

What’s Next?

This is only the tip of the iceberg. We are at version 1 of WAQL, and you can expect more to come in the future. Already, there are over 450 different properties you can access and query through over 60 different object types. You can combine as many statements as you want, in any order — the possibilities are already infinite today. There are also other keywords we did not mention in this article.

The List View is a good place to practice using WAQL. You can also use the Query Editor to save your WAQL queries in your project.

WAQL_Query

To learn more about WAQL and everything it supports, refer to the WAQL reference.

If you are a WAAPI user, you can also use WAQL directly inside the function ak.wwise.core.object.get. It will make your life easier.

Tell us what you were able to do with WAQL!

Bernard Rodrigue

Director, Wwise Experience

Audiokinetic

Bernard Rodrigue

Director, Wwise Experience

Audiokinetic

Bernard Rodrigue is Director, Wwise Experience at Audiokinetic. He joined Audiokinetic in 2005 and actively participated in developing the foundations of Wwise. Today, Bernard continues to lead several projects related to the advancement and expansion of Wwise.

 @decasteljau

Comments

Leave a Reply

Your email address will not be published.

More articles

Don't Even Ask How I Did It! Instructions for the 5-Minute Entry-Level Track Swapping Challenge

Here are the steps I took during my live 5-minute challenge, along with a couple of tips about the...

28.5.2019 - By George A. Sanger

5-Minute Entry-Level Track Swapping Challenge

Can You (or this Entry-Level Legend in a Lab Coat?) Implement Track Swapping in Five Minutes?

29.5.2019 - By George A. Sanger

Event-Based Packaging Process Overview

What is Event-Based Packaging? Not long ago, the UE4 Integration of Wwise 2019.2 launched a new...

21.1.2021 - By Fan Runpeng

9 Simple Steps to Authoring and Profiling Audio Objects in Wwise

So, you're interested in taking a look at the new Object-based Audio pipeline in Wwise but don't...

8.4.2021 - By Damian Kastbauer

State of the Wwise Unreal Integration

This post is a follow-up to my previous "Improving the Wwise Unreal Integration" published last...

13.10.2023 - By Guillaume Renaud

Wwise Release Cycle News | Simultaneous Patch Releases & Development Support for Unreal Engine Preview

This post aims to share some changes we made to our development process in the past few months....

14.5.2024 - By Guillaume Renaud

More articles

Don't Even Ask How I Did It! Instructions for the 5-Minute Entry-Level Track Swapping Challenge

Here are the steps I took during my live 5-minute challenge, along with a couple of tips about the...

5-Minute Entry-Level Track Swapping Challenge

Can You (or this Entry-Level Legend in a Lab Coat?) Implement Track Swapping in Five Minutes?

Event-Based Packaging Process Overview

What is Event-Based Packaging? Not long ago, the UE4 Integration of Wwise 2019.2 launched a new...