Last active
June 6, 2023 13:00
-
-
Save ak-brodrigue/b98d12e67167eb0e00291cd9c2c02164 to your computer and use it in GitHub Desktop.
Creating Music objects with Wwise 2023.1+
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2023 Audiokinetic Inc. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
from waapi import WaapiClient | |
from pathlib import Path | |
import json, tempfile, os, shutil | |
def Object(type, name, *children, **properties): | |
# Create the definition of a generic Wwise Object for ak.wwise.core.object.set | |
# For type list, refer to: | |
# https://www.audiokinetic.com/en/library/edge/?source=SDK&id=wobjects_index.html | |
# - children are taken as variable arguments | |
# - properties are taken as keyword arguments | |
# Basic definition | |
definition = { | |
"type" : type, | |
"name" : name | |
} | |
# Take any keyword argument, and convert it to a property | |
for key, value in properties.items(): | |
definition['@'+key] = value | |
# Children can either be other definitions or file import instructions | |
if len(children) > 0 and isinstance(children[0], str): | |
# When we find a string as the first argument, we consider a file path | |
definition["import"] = {} | |
definition["import"]["files"] = [] | |
for file in children: | |
definition["import"]["files"].append({ | |
"audioFile" : file, | |
}) | |
else: | |
# Else, we consider an array of children definitions | |
definition["children"] = children | |
return definition | |
def MusicSegment(name, *children, **properties): | |
# Create the definition for a Music Segment | |
return Object("MusicSegment", name, *children, **properties) | |
def MusicTrack(name, *children, **properties): | |
# Create the definition for a Music Track | |
return Object("MusicTrack", name, *children, **properties) | |
def MusicSwitchContainer(path, arguments, associations, *children, **properties): | |
# Create the definition for a Music Switch Container | |
path = Path(path) | |
name = path.stem | |
def build_path(idx_and_value): | |
index, value = idx_and_value | |
if value == '*': | |
return arguments[index] | |
return arguments[index] + '\\' + value | |
# Build the entries | |
entries = [] | |
for key, value in associations.items(): | |
tokens = enumerate(key.split('.')) | |
entry = Object("MultiSwitchEntry", '', | |
EntryPath = list(map( build_path, tokens)), | |
AudioNode = str(path.joinpath(value))) | |
entries.append(entry) | |
properties = properties or {} | |
properties['Arguments'] = arguments | |
properties['Entries'] = entries | |
return Object("MusicSwitchContainer", name, *children, **properties) | |
def MusicPlaylistContainer(name, *children, **properties): | |
# Create the definition for a Music Playlist Container | |
return Object("MusicPlaylistContainer", name, *children, **properties) | |
def MusicPlaylistItem(*children, **properties) : | |
# Create the definition for a Music Playlist Item | |
if 'Segment' in properties: | |
properties['PlaylistItemType'] = 1 | |
return Object("MusicPlaylistItem", '', *children, **properties) | |
def MusicCue(name, *children, **properties): | |
# Create the definition for a Music Playlist Container | |
return Object("MusicCue", name, *children, **properties) | |
def MusicTransition(*children, **properties): | |
# Create the definition for a Music Transition | |
return Object("MusicTransition", '', *children, **properties) | |
def SwitchGroup(name, *children, **properties): | |
return Object("SwitchGroup", name, *children, **properties) | |
def Switch(name, *children, **properties): | |
return Object("Switch", name, *children, **properties) | |
# Connect (default URL) | |
client = WaapiClient() | |
# Generate some WAV files for demonstration | |
temp_dir = tempfile.TemporaryDirectory(prefix='WAV-import-') | |
frequencies = [100, 250, 300, 400, 700] | |
wav_args = list(map(lambda f: { "path" : os.path.join(str(temp_dir.name), "SFX_" + str(f) + ".wav"), "frequency": f, "waveform": "square" }, frequencies)) | |
for arg in wav_args: | |
client.call("ak.wwise.debug.generateToneWAV", arg) | |
# Prepare the music hierarchy definition | |
root = R"\Interactive Music Hierarchy\Default Work Unit" | |
set_args = { | |
"objects": [ | |
{ | |
"object":R"\Switches\Default Work Unit", | |
"children":[ | |
SwitchGroup("SWG_1", | |
Switch("SW_1_1"), | |
Switch("SW_1_2"), | |
), | |
SwitchGroup("SWG_2", | |
Switch("SW_2_1"), | |
Switch("SW_2_2"), | |
) | |
] | |
}, | |
{ | |
"object": root, | |
"children": [ | |
MusicSwitchContainer(root + R"\MSC", | |
# The switches on which the music container is subscribed | |
[ | |
R"\Switches\Default Work Unit\SWG_1", | |
R"\Switches\Default Work Unit\SWG_2" | |
], | |
# The association entries | |
{ | |
R"SW_1_1.SW_2_1": R"MPL1", | |
R"SW_1_2.SW_2_1": R"MPL1", | |
R"*.SW_2_2": R"MPL2", | |
}, | |
# The children | |
MusicPlaylistContainer( | |
R"MPL1", | |
MusicSegment("Segment1", | |
MusicTrack("TrackA", wav_args[0]["path"]), # Path to WAV file | |
MusicTrack("TrackB", wav_args[1]["path"]), # Path to WAV file | |
Cues = [ | |
MusicCue('', CueType = 0, TimeMs=0), | |
MusicCue('', CueType = 1, TimeMs=1000) | |
] | |
), | |
MusicSegment("Segment2", | |
wav_args[2]["path"], # Path to WAV file | |
Cues = [ | |
MusicCue('', CueType = 0, TimeMs=0), # Entry | |
MusicCue('', CueType = 1, TimeMs=1000), # Exit | |
MusicCue('Hello', CueType = 2, TimeMs=500) # Custom | |
]), | |
PlaylistRoot = | |
MusicPlaylistItem( | |
MusicPlaylistItem( Segment = root + "\MSC\MPL1\Segment1"), | |
MusicPlaylistItem( Segment = root + "\MSC\MPL1\Segment2"), | |
LoopCount = 0 | |
) | |
), | |
MusicPlaylistContainer( | |
R"MPL2", | |
MusicSegment("Segment1", | |
wav_args[3]["path"], # Path to WAV file | |
Volume = -3), | |
MusicSegment("Segment2", | |
wav_args[4]["path"]), # Path to WAV file | |
PlaylistRoot = | |
MusicPlaylistItem( | |
MusicPlaylistItem( Segment = root + "\MSC\MPL2\Segment1"), | |
MusicPlaylistItem( Segment = root + "\MSC\MPL2\Segment2"), | |
LoopCount = 0 | |
) | |
), | |
Volume = -12, | |
TransitionRoot = MusicTransition( | |
MusicTransition(), # default | |
MusicTransition( | |
SourceContextType = 2, # Object | |
SourceContextObject = root + R"\MSC\MPL1", | |
ExitSourceAt = 0, # Immediate | |
) | |
) | |
) | |
] | |
} | |
], | |
"onNameConflict": "merge", | |
"listMode":"replaceAll" | |
} | |
# Create the music hierarchy with the provided definition | |
result = client.call("ak.wwise.core.object.set", set_args) | |
print( json.dumps(result) ) | |
# Disconnect | |
client.disconnect() | |
# clean up temp files | |
shutil.rmtree(temp_dir.name) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment