PyWwise: WAAPI Made Pythonic

Audio Programming

Prologue

Greetings, dear reader! My name is Matheus Vilano. I'm a game audio educator and developer specialized in asset pipelines, development tools, and gameplay systems – usually within the realm of audio. You might recognize my name, and I bet it’s likely from this Wwise Up On Air video.

I wrote this blog post to help game audio professionals and enthusiasts learn more about the Wwise Authoring API (WAAPI) and the PyWwise Python package. We’ll begin by illustrating common WAAPI use cases, then dive into PyWwise. My goal is that after reading this article, you’ll feel empowered to build WAAPI tools in Python.

Before we get started, I’d like to thank Audiokinetic for this opportunity! It’s always a pleasure to collaborate, and I hope to keep bringing new content to the game audio community. Y’all are awesome!

If you plan to follow along, you’ll need:

You can technically use any Integrated Development Environment (IDE) or code editor — including Visual Studio Code — but I will stick to PyCharm, as it’s Python-focused and very easy to use. It can even handle installing and setting up Python!

Why Python?

PyWwise is written exclusively for Python. I chose Python because it excels in three areas:

  • Code readability
  • Built-in libraries
  • Community support

And if you’re new to Python, don’t worry — the examples here are beginner-friendly, and Python’s official website has tons of great resources (e.g. articles, book recommendations, documentation, etc.).

What is WAAPI?

The Wwise Authoring API (WAAPI) is a language-agnostic interface that lets tools and scripts communicate with Wwise. It’s widely used in the making of development tools, usually involving automation, validation, and integration tasks.

Here are a few simple tool ideas, for illustration purposes:

  • An importer that creates containers based on audio file names.
  • A script that replaces spaces in object names with underscores.
  • A batch script that trims tail silence for optimization.
  • A cleanup tool that deletes any Wwise object not in a SoundBank.

WAAPI works really well with the Wwise Authoring Query Language (WAQL) and Wwise Add-On Commands. Mastering these three can open up tons of new possibilities when writing audio tools.

What is PyWwise?

PyWwise is a free and open-source Python package, built on top of Audiokinetic’s waapi-client package, which handles all communication with Wwise. Throughout this article, I’ll refer to waapi-client as "raw WAAPI".

Motivation

I’ve used WAAPI extensively in my career to empower teams of all sizes with all sorts of custom tools. Naturally, I became aware of the drawbacks of raw WAAPI:

  • Requires heavy use of JSON objects, often quite convoluted.
  • Lacks support for code completion technologies, which can be a time sink.
  • Offers no inline documentation, instead requiring a web browser.

“You can fix those issues by writing wrapper functions”, some of you might be thinking. Yes, yes – I know. And that is exactly what I was doing before PyWwise: over and over again, for different projects and clients. You see the problem?

Eventually, I got tired of writing boilerplate code. I realised I was spending more time doing JSON acrobatics than actually writing productive code. I wanted a world where I (and everyone else) didn’t have to do that anymore.

When I finally had some downtime in between projects, PyWwise came to be. An ambitious passion project. The ultimate wrapper – the power-up that raw WAAPI needed. This is PyWwise.

Initial Setup

Let’s get set up! Open PyCharm and create a new project. You can refer to the screenshots attached (which are all based on version 2025.1 and hopefully not too different from future versions).

welcome-to-pycharm

python-new-project

python-package-pywwise

Once you’re ready, we’ll walk through some code. Each example follows this structure:

  1. A high-level description of a task.
  2. How to implement it with raw WAAPI.
  3. How to implement it with PyWwise.

Practical Examples

Example 1: Hello, Wwise!

Let’s start with something simple: writing a log message to the Wwise Logs view. In case you are not familiar with the Logs view in Wwise, here’s where you can open it:

logs-view-open-Wwise

Luckily, once we’re connected to Wwise, we only need a single function call to add a new log item with a message. We’ll need a few data inputs (arguments) for that:

  • “message” - The text of the new log entry.
  • ”severity” - The severity (type) of the new log entry. This has to be one of these values: “Message”, “Warning”, “Error, or “Fatal Error”.
  • “channel” - The channel (tab) of the new log entry. This has to be one of these values: “soundbankGenerate”, “conversion”, “copyPlatformSettings”, “waapi”, “projectLoad”, “general”, “sourceControl”, or “lua”.

First, let’s take a look at an implementation using raw WAAPI:

 

# Raw WAAPI

from waapi import WaapiClient

# First, we need to connect to Wwise.
client = WaapiClient()

# Next, we need to build a JSON object with our data inputs.
args = {"message": "Hello, Wwise!", "severity": "Message", "channel": "general"}

# Then, we can call the function (by name), using our JSON object.
client.call("ak.wwise.core.log.addItem", args)

# Finally, we can disconnect.
client.disconnect()

Notice how there are a lot of strings there: 

  • The names of the data inputs (“message”, “severity”, and “channel”).
  • The data inputs themselves (“Hello, Wwise!”, “Message”, and “general”).
  • The name of the WAAPI function to call (“ak.wwise.core.log.addItem”).

This opens up room for errors: typos which IDEs cannot reliably catch. PyWwise addresses this workflow issue by handling the creation of strings and JSON objects for you

# PyWwise

from pywwise import new_waapi_connection, ELogSeverity, ELogChannel

# First, we need to connect to Wwise.
ak = new_waapi_connection()

# Now we just need to call the function - no JSON needed!
ak.wwise.core.log.add_item("Hello, Wwise!", ELogSeverity.MESSAGE, ELogChannel.GENERAL)

# Finally, we can disconnect.
ak.disconnect()

Now the only string left is the actual message: “Hello, Wwise!”. The others are replaced with actual Python objects, which are fully self-documented and support auto-completion. For example, here is the documentation for the ak.wwise.core.log.add_item function, directly inside PyCharm (by hovering over the function name):

hello-wwise-pywwise

You likely noticed a couple of objects with the prefix "E". Those are called enumerations. You can think of an enumeration as a collection of pre-determined values. The enumeration ELogSeverity, for example, offers exactly 4 values — and you can pick just one: MESSAGE, WARNING, ERROR, or FATAL_ERROR. The enumerations provided by PyWwise allow for auto-completion and error-checking (e.g. typos)!

pywwise-general

pywwise-general-typo

Finally, running either PyWwise code snippet will print a new “Hello, Wwise!” log entry directly in Wwise. Easy, right?

pywwise-log-entry-Wwise

Now, let’s look at some examples involving Wwise objects (e.g. Sounds, Containers, Buses, etc). Most WAAPI-powered tools and scripts involve finding, creating, deleting, and/or modifying Wwise objects.

Example 2: Finding Wwise Objects

Let’s search for all Sound objects that are set to loop and then enable streaming on each of them. Fortunately, finding objects is really easy with the Wwise Authoring Query Language (WAQL). As for setting the streaming property, we will need 3 data inputs:

  • "object" - The object on which to set the new property value.
  • "property" - The name of the property we are setting a new value for.
  • "value" - The new value of the property.

Do note that this article does not aim to discuss query languages in depth — but I highly recommend you learn WAQL inside-out, as querying for Wwise objects is a very common operation when working with WAAPI (raw or PyWwise). This example, though, will stick to using a very simple query.

Let’s get right to it, starting with raw WAAPI:

# Raw WAAPI

from waapi import WaapiClient

# Connect to Wwise.
client = WaapiClient()

# Find all Sound objects that are set to loop.
args_get = {"waql": '$ from type Sound where IsLoopingEnabled = true'}
objs = client.call("ak.wwise.core.object.get", args_get)

# Go over each object, making sure it is set to be streamed.
for obj in objs["return"]:
  args_set = {"object": obj["id"], "property": "IsStreamingEnabled", "value": True}
  client.call("ak.wwise.core.object.setProperty", args_set)

# Done!
client.disconnect()

Using PyWwise can simplify the process quite a bit by eliminating JSON and minimizing the amount of strings:

# PyWwise

from pywwise import new_waapi_connection, Sound

# Connect to Wwise.
ak = new_waapi_connection()

# Find all Sound objects that are set to loop.
objs = ak.wwise.core.object.get('$ from type Sound where IsLoopingEnabled = true')

# Go over each object, making sure it is set to be streamed.
for obj in objs:
  ak.wwise.core.object.set_property(obj.guid, Sound.is_streaming_enabled, True)

# Done!
ak.disconnect()

As you may have noticed, this script is a good example of a scalable tool: it will work with as many Sound objects as you may have. From one to one zillion — it really does not matter.

sound-objects-script

sound-objects-scriptable-after

Example 3: Creating Wwise Objects

Now let’s create a Sound object and set its volume, looping and streaming parameters. We will also set its output bus. To create the object, we will need a few data inputs:

  • “name” - The new object’s name.
  • “type” - The type of object to create. Has to be one of the object names listed in the Wwise Objects Reference.
  • “parent” - The GUID, project path, or unique name of the existing object inside which to create a new object.
  • “onNameConflict” - What to do in case an object with the same parent and name already exists. Has to be “replace”, “rename”, “merge”, or “fail”.

As for the parameters, there are two types: properties and references. A property is a parameter that usually stores a boolean (e.g. True) or numeric value (e.g. -6.0). A reference is a parameter that usually stores the Project Path or GUID of another Wwise object (e.g. a Bus at “/Master-Mixer Hierarchy/Default Work Unit/Master Audio Bus/Sfx”). For setting parameters, we’ll need:

  • “object” - The project path or GUID or the object we’re setting the parameter on.
  • “property” or “reference” - The name of the parameter we’re setting. You must consult the Wwise Objects Reference.
  • “value” - The parameter’s new value.

Let’s see how raw WAAPI compares to PyWwise, in this case:

# Raw WAAPI

from waapi import WaapiClient

# New connection.
client = WaapiClient()

# JSON object with data inputs. The parent object is the Default Work Unit in the Actor-Mixer Hierarchy.
parent = "\\Actor-Mixer Hierarchy\\Default Work Unit"
args_new = {"name": "MyCoolSfx", "type": "Sound", "parent": parent, "onNameConflict": "replace"}

# Function call (by name).
obj = client.call("ak.wwise.core.object.create", args_new)

# Set Volume property.
args_volume = {"object": obj["id"], "property": "Volume", "value": -6.0}
client.call("ak.wwise.core.object.setProperty", args_volume)

# Set IsLoopingEnabled property.
args_loop = {"object": obj["id"], "property": "IsLoopingEnabled", "value": True}
client.call("ak.wwise.core.object.setProperty", args_loop)

# Set IsStreamingEnabled property.
args_stream = {"object": obj["id"], "property": "IsStreamingEnabled", "value": True}
client.call("ak.wwise.core.object.setProperty", args_stream)

# Set OutputBus reference.
path_bus = "/Master-Mixer Hierarchy/Default Work Unit/Master Audio Bus/Sfx"  # The bus MUST exist!
args_bus = {"object": obj["id"], "reference": "OutputBus", "value": path_bus}
client.call("ak.wwise.core.object.setReference", args_bus)

# Done!
client.disconnect()

The same implementation in PyWwise looks considerably cleaner: 

# PyWwise

from pywwise import new_waapi_connection, ProjectPath, EObjectType, ENameConflictStrategy, Sound

# New connection.
ak = new_waapi_connection()

# ProjectPath has a function that can give us the Default Work Unit within the Actor-Mixer Hierarchy.
parent = ProjectPath.actor_mixer_hierarchy(default_work_unit=True)

# This function creates a new object and returns some info about it.
# We will need that info, so let's store it in a variable.
info = ak.wwise.core.object.create("MyCoolSfx", EObjectType.SOUND, parent, ENameConflictStrategy.REPLACE)

# We will use the object's info to create a direct connection to it.
# This lets us easily set properties and references on that object.
sound = Sound(info, ak)
sound.volume = -6.0
sound.is_looping_enabled = True
sound.is_streaming_enabled = True
sound.output_bus = ProjectPath("/Master-Mixer Hierarchy/Default Work Unit/Master Audio Bus/Sfx")

# Done!
ak.disconnect()

And that’s it! Out of the examples we have worked with, this is arguably the most useful one. A lot of tools for Wwise automation — including many of my own tools — involve code similar to the snippets showcased in this section.

Takeaways

PyWwise doesn’t just simplify syntax. It makes code safer and easier to write:

  • Fewer strings to mistype.
  • Enumerations with autocomplete.
  • Full inline documentation.

You’ll spend less time double-checking docs and more time building what matters.

Now, should every team switch to PyWwise? Not necessarily. If your team has your own WAAPI wrapper and it’s working well, stick with it — but if you’re starting fresh or looking to drastically simplify your codebase, PyWwise might be what you’ve been looking for.

Conclusion

I hope you enjoyed learning about PyWwise! For both new learners and seasoned WAAPI veterans, the PyWwise GitHub Wiki has tons of examples and guides. It’s updated regularly with every major update of PyWwise, with the help of contributors.

If you ever have any questions about PyWwise, feel free to reach out! And if you ever find a bug or think of a potential enhancement, please do not hesitate to post it on the PyWwise GitHub Issues page. Pull requests are also absolutely welcome!

Thank you for reading, and see you next time!

Matheus Vilano

Matheus Vilano

Matheus Vilano is a game developer specialized in technical audio, with a focus on systems design and tools development. Some of his credits include Until Dawn, Glassbreakers, and Baldur’s Gate 3. Matheus also maintains open-source projects, teaches game development, and produces LoFi music under the artist name “Lonyeld”.

Comments

Leave a Reply

Your email address will not be published.

More articles

A Step-by-Step WAAPI Example

In my previous article, we reviewed a few of the various possibilities where the Wwise Authoring...

9.5.2017 - By Bernard Rodrigue

A New Multiband Compressor Plug-in for Wwise: Polyspectral MBC joins the Audiokinetic Community Partner Program!

Polyspectral MBC is a new multi-band compressor plug-in for Wwise. I’m excited to be releasing it...

26.6.2018 - By Ethan Fenn

WAAPI is for Everyone | Part 1: Overview

Hello. I’m Thomas Wang (also known as Xi Ye). I discovered WAAPI (Wwise Authoring API) in the second...

13.11.2020 - By Thomas Wang (汪洋)

Coding Wwise in UE for Beginners

Introduction Welcome to the fellow coders (or anyone, let’s not limit ourselves!) With this article,...

10.8.2023 - By Murat Özcan

Custom Wwise Listener Position Projection System for Tilted 2D View

Introduction This is the 2nd of a 3-part tech-blog series by Jater (Ruohao) Xu, sharing the work...

26.8.2024 - By Ruohao (Jater) Xu

Introduction to Using WAAPI with UE5 and C++

Here is all you need to know to get started using WAAPI with Unreal. For the most part, this article...

3.12.2024 - By Javier Zumer

More articles

A Step-by-Step WAAPI Example

In my previous article, we reviewed a few of the various possibilities where the Wwise Authoring...

A New Multiband Compressor Plug-in for Wwise: Polyspectral MBC joins the Audiokinetic Community Partner Program!

Polyspectral MBC is a new multi-band compressor plug-in for Wwise. I’m excited to be releasing it...

WAAPI is for Everyone | Part 1: Overview

Hello. I’m Thomas Wang (also known as Xi Ye). I discovered WAAPI (Wwise Authoring API) in the second...