├── .gitreview ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── Editor.meta ├── Editor ├── AK.Wwise.Unity.Addressables.Editor.asmdef ├── AK.Wwise.Unity.Addressables.Editor.asmdef.meta ├── AddressableMetadata.cs ├── AddressableMetadata.cs.meta ├── AddressableSymbolDefiner.cs ├── AddressableSymbolDefiner.cs.meta ├── AkAddressablesEditorSettings.cs ├── AkAddressablesEditorSettings.cs.meta ├── AkAddressablesEditorUtilities.cs ├── AkAddressablesEditorUtilities.cs.meta ├── BuildScriptWwisePacked.cs ├── BuildScriptWwisePacked.cs.meta ├── Samples.meta ├── Samples │ ├── WwiseAssetMetadataPreserver.cs │ └── WwiseAssetMetadataPreserver.cs.meta ├── SceneUpdater.cs ├── SceneUpdater.cs.meta ├── WwiseAssetImportProcessors.cs ├── WwiseAssetImportProcessors.cs.meta ├── WwiseBankImporter.cs ├── WwiseBankImporter.cs.meta ├── WwiseStreamingAssetImporter.cs └── WwiseStreamingAssetImporter.cs.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── AK.Wwise.Unity.Addressables.asmdef ├── AK.Wwise.Unity.Addressables.asmdef.meta ├── AkAddressableBankManager.cs ├── AkAddressableBankManager.cs.meta ├── AkAddressablesSoundEngineInitialization.cs ├── AkAddressablesSoundEngineInitialization.cs.meta ├── AkAssetUtilities.cs ├── AkAssetUtilities.cs.meta ├── AkWwiseAddressablesInitializationSettings.cs ├── AkWwiseAddressablesInitializationSettings.cs.meta ├── InitBankHolder.cs ├── InitBankHolder.cs.meta ├── WwiseAddressableSoundBank.cs ├── WwiseAddressableSoundBank.cs.meta ├── WwiseAsset.cs ├── WwiseAsset.cs.meta ├── WwiseInitBankReference.cs ├── WwiseInitBankReference.cs.meta ├── WwiseSoundBankAsset.cs ├── WwiseSoundBankAsset.cs.meta ├── WwiseStreamingMediaAsset.cs └── WwiseStreamingMediaAsset.cs.meta ├── package.json └── package.json.meta /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=git.hq.audiokinetic.com 3 | project=WwiseUnityAddressables 4 | defaultremote=origin -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audiokinetic/WwiseUnityAddressables/312651c78ddd2ab2f39a80828a1874d35813da30/CHANGELOG.md -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e25cefb3100359247b9f58e8d43410a1 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 75de3229038472248a29fa473c63184a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/AK.Wwise.Unity.Addressables.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AK.Wwise.Unity.Addressables.Editor", 3 | "rootNamespace": "", 4 | "references": [ 5 | "AK.Wwise.Unity.MonoBehaviour.Editor", 6 | "Unity.Addressables.Editor", 7 | "Unity.Addressables", 8 | "Unity.ScriptableBuildPipeline.Editor", 9 | "Unity.ResourceManager", 10 | "Unity.ScriptableBuildPipeline", 11 | "AK.Wwise.Unity.API", 12 | "AK.Wwise.Unity.Addressables", 13 | "AK.Wwise.Unity.API.WwiseTypes", 14 | "AK.Wwise.Unity.ProjectDatabase" 15 | ], 16 | "includePlatforms": [ 17 | "Editor" 18 | ], 19 | "excludePlatforms": [], 20 | "allowUnsafeCode": false, 21 | "overrideReferences": false, 22 | "precompiledReferences": [], 23 | "autoReferenced": true, 24 | "defineConstraints": [ "!UNITY_SERVER" ], 25 | "versionDefines": [ 26 | { 27 | "name": "com.unity.addressables", 28 | "expression": "1.8", 29 | "define": "UNITY_ADDRESSABLES" 30 | }, 31 | { 32 | "name": "com.audiokinetic.wwise.addressables", 33 | "expression": "1.0.0", 34 | "define": "AK_WWISE_ADDRESSABLES" 35 | } 36 | ], 37 | "noEngineReferences": false 38 | } -------------------------------------------------------------------------------- /Editor/AK.Wwise.Unity.Addressables.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 005b486a0b1ff814abc66a82ac38965e 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/AddressableMetadata.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | using System.Collections.Generic; 19 | using UnityEngine; 20 | using UnityEditor; 21 | 22 | 23 | namespace AK.Wwise.Unity.WwiseAddressables 24 | { 25 | public class AddressableMetadata : UnityEngine.ScriptableObject 26 | { 27 | public string groupName; 28 | public List labels = new List(); 29 | 30 | public AddressableMetadata() 31 | { 32 | labels = new List(labels); 33 | } 34 | 35 | public AddressableMetadata(List inLabels, string inGroupName) 36 | { 37 | labels = inLabels; 38 | groupName = inGroupName; 39 | } 40 | 41 | public bool IsDifferent(AddressableMetadata other) 42 | { 43 | return other.groupName != groupName || other.labels != labels; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Editor/AddressableMetadata.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 613af5da8070b134ca7e9841a4c5eac2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/AddressableSymbolDefiner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using UnityEditor; 4 | 5 | [InitializeOnLoad] 6 | public static class AddressableSymbolDefiner 7 | { 8 | private const string CurrentVersion = "WWISE_ADDRESSABLES_24_1_OR_LATER"; 9 | 10 | static AddressableSymbolDefiner() 11 | { 12 | 13 | if (PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone).Contains(CurrentVersion)) 14 | { 15 | return; 16 | } 17 | #if !WWISE_2024_OR_LATER 18 | return; 19 | #endif 20 | foreach (BuildTargetGroup targetGroup in Enum.GetValues(typeof(BuildTargetGroup))) 21 | { 22 | if (targetGroup != BuildTargetGroup.Unknown) 23 | { 24 | AddDefineSymbols(targetGroup); 25 | } 26 | } 27 | } 28 | 29 | private static void AddDefineSymbols(BuildTargetGroup targetGroup) 30 | { 31 | string currentDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup); 32 | var currentDefineList = currentDefines.Split(';').ToList(); 33 | 34 | if (!currentDefineList.Contains(CurrentVersion)) 35 | { 36 | currentDefineList.Add(CurrentVersion); 37 | string updatedDefines = string.Join(";", currentDefineList); 38 | PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, updatedDefines); 39 | } 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Editor/AddressableSymbolDefiner.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e8b83d0297902d246963546fadf43bf6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/AkAddressablesEditorSettings.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if UNITY_EDITOR 19 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 20 | using UnityEngine; 21 | using System.Xml; 22 | 23 | namespace AK.Wwise.Unity.WwiseAddressables 24 | { 25 | [System.Serializable] 26 | public class AkAddressablesSettings 27 | { 28 | public const string Filename = "WwiseAddressablesSettings.xml"; 29 | 30 | public static string Path 31 | { 32 | get { return System.IO.Path.Combine(UnityEngine.Application.dataPath, Filename); } 33 | } 34 | 35 | public static bool Exists { get { return System.IO.File.Exists(Path); } } 36 | 37 | public bool UseSampleMetadataPreserver; 38 | public string MetadataPath; 39 | 40 | 41 | internal static AkAddressablesSettings LoadSettings() 42 | { 43 | var settings = new AkAddressablesSettings(); 44 | 45 | try 46 | { 47 | var path = Path; 48 | if (System.IO.File.Exists(path)) 49 | { 50 | var xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(AkAddressablesSettings)); 51 | using (var xmlFileStream = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read)) 52 | settings = xmlSerializer.Deserialize(xmlFileStream) as AkAddressablesSettings; 53 | } 54 | else 55 | { 56 | var projectDir = System.IO.Path.GetDirectoryName(UnityEngine.Application.dataPath); 57 | var foundWwiseProjects = System.IO.Directory.GetFiles(projectDir, "*.wproj", System.IO.SearchOption.AllDirectories); 58 | 59 | //WG-61124 In order to avoid addressables not being updated when removing the streaming option, enable the RemoveUnusedGeneratedFiles feature 60 | foreach (var projects in foundWwiseProjects) 61 | { 62 | var doc = new System.Xml.XmlDocument(); 63 | doc.Load(projects); 64 | var RemoveUnusedGeneratedFilesNode = doc.SelectSingleNode("//Property[@Name='RemoveUnusedGeneratedFiles']"); 65 | if (RemoveUnusedGeneratedFilesNode != null) 66 | { 67 | XmlAttribute valueAttribute = RemoveUnusedGeneratedFilesNode.Attributes["Value"]; 68 | if (valueAttribute != null && valueAttribute.Value != null && valueAttribute.Value != "True") 69 | { 70 | valueAttribute.Value = "True"; 71 | doc.Save(projects); 72 | } 73 | } 74 | 75 | } 76 | 77 | 78 | 79 | settings.MetadataPath = "WwiseAddressablesMetadata"; 80 | settings.UseSampleMetadataPreserver = true; 81 | } 82 | } 83 | catch (System.Exception exception) 84 | { 85 | Debug.LogWarning("Could not load Wwise Addressables settings"); 86 | Debug.LogWarning(exception); 87 | } 88 | 89 | if (string.IsNullOrEmpty(settings.MetadataPath)) 90 | { 91 | settings.MetadataPath = "WwiseAddressablesMetadata"; 92 | } 93 | return settings; 94 | } 95 | 96 | public void SaveSettings() 97 | { 98 | try 99 | { 100 | var xmlDoc = new System.Xml.XmlDocument(); 101 | var xmlSerializer = new System.Xml.Serialization.XmlSerializer(GetType()); 102 | using (var xmlStream = new System.IO.MemoryStream()) 103 | { 104 | var streamWriter = new System.IO.StreamWriter(xmlStream, System.Text.Encoding.UTF8); 105 | xmlSerializer.Serialize(streamWriter, this); 106 | xmlStream.Position = 0; 107 | xmlDoc.Load(xmlStream); 108 | xmlDoc.Save(Path); 109 | } 110 | } 111 | catch 112 | { 113 | UnityEngine.Debug.LogErrorFormat("WwiseUnity: Unable to save addressables settings to file <{0}>. Please ensure that this file path can be written to.", Path); 114 | } 115 | } 116 | } 117 | 118 | public class AkAddressablesEditorSettings 119 | { 120 | private static AkAddressablesSettings s_Instance; 121 | 122 | public static AkAddressablesSettings Instance 123 | { 124 | get 125 | { 126 | if (s_Instance == null) 127 | s_Instance = AkAddressablesSettings.LoadSettings(); 128 | return s_Instance; 129 | } 130 | } 131 | 132 | public static void Reload() 133 | { 134 | s_Instance = AkAddressablesSettings.LoadSettings(); 135 | } 136 | 137 | #region GUI 138 | class SettingsProvider : UnityEditor.SettingsProvider 139 | { 140 | class Styles 141 | { 142 | public static string AddressablesSettings = "Asset metadata preservation"; 143 | 144 | public static UnityEngine.GUIContent UseSampleMetadataPreserver = new UnityEngine.GUIContent("Use Sample Metadata Preserver", "Use the sample metadata preserver to preserve Wwise addressable asset groups and labels when they are deleted."); 145 | 146 | public static UnityEngine.GUIContent MetadataPath = new UnityEngine.GUIContent("Wwise Asset Metadata Path", "Location to create the assets that will contain the addressable asset metadata."); 147 | 148 | 149 | private static UnityEngine.GUIStyle textField; 150 | public static UnityEngine.GUIStyle TextField 151 | { 152 | get 153 | { 154 | if (textField == null) 155 | textField = new UnityEngine.GUIStyle("textfield"); 156 | return textField; 157 | } 158 | } 159 | } 160 | 161 | private static bool Ellipsis() 162 | { 163 | return UnityEngine.GUILayout.Button("...", UnityEngine.GUILayout.Width(30)); 164 | } 165 | 166 | private SettingsProvider(string path) : base(path, UnityEditor.SettingsScope.Project) { } 167 | 168 | [UnityEditor.SettingsProvider] 169 | public static UnityEditor.SettingsProvider CreateMyCustomSettingsProvider() 170 | { 171 | return new SettingsProvider("Project/Wwise Addressables") { keywords = GetSearchKeywordsFromGUIContentProperties() }; 172 | } 173 | 174 | public override void OnGUI(string searchContext) 175 | 176 | { 177 | bool changed = false; 178 | 179 | var labelWidth = UnityEditor.EditorGUIUtility.labelWidth; 180 | UnityEditor.EditorGUIUtility.labelWidth += 100; 181 | 182 | var settings = Instance; 183 | 184 | UnityEngine.GUILayout.Space(UnityEditor.EditorGUIUtility.standardVerticalSpacing); 185 | UnityEngine.GUILayout.Label(Styles.AddressablesSettings, UnityEditor.EditorStyles.boldLabel); 186 | UnityEngine.GUILayout.Space(UnityEditor.EditorGUIUtility.standardVerticalSpacing); 187 | 188 | UnityEditor.EditorGUI.BeginChangeCheck(); 189 | 190 | using (new UnityEngine.GUILayout.VerticalScope("box")) 191 | { 192 | bool newValue = UnityEditor.EditorGUILayout.Toggle(Styles.UseSampleMetadataPreserver, settings.UseSampleMetadataPreserver); 193 | if (settings.UseSampleMetadataPreserver != newValue) 194 | { 195 | settings.UseSampleMetadataPreserver = newValue; 196 | if (settings.UseSampleMetadataPreserver) 197 | { 198 | WwiseAddressableAssetMetadataPreserver.BindMetadataDelegate(); 199 | } 200 | else 201 | { 202 | WwiseAddressableAssetMetadataPreserver.UnbindMetaDataDelegate(); 203 | } 204 | } 205 | UnityEngine.GUILayout.Space(UnityEditor.EditorGUIUtility.standardVerticalSpacing); 206 | 207 | using (new UnityEngine.GUILayout.HorizontalScope()) 208 | { 209 | 210 | 211 | if (settings.UseSampleMetadataPreserver) 212 | { 213 | UnityEditor.EditorGUILayout.PrefixLabel(Styles.MetadataPath); 214 | UnityEditor.EditorGUILayout.SelectableLabel(settings.MetadataPath, Styles.TextField, UnityEngine.GUILayout.Height(17)); 215 | if (Ellipsis()) 216 | { 217 | var OpenInPath = System.IO.Path.GetDirectoryName(AkUtilities.GetFullPath(UnityEngine.Application.dataPath, settings.MetadataPath)); 218 | var MetadataPathNew = UnityEditor.EditorUtility.OpenFolderPanel("Select your metadata Project", OpenInPath, "WwiseAddressableMetadata"); 219 | if (MetadataPathNew.Length != 0) 220 | { 221 | settings.MetadataPath = AkUtilities.MakeRelativePath(UnityEngine.Application.dataPath, MetadataPathNew); 222 | changed = true; 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | 230 | if (UnityEditor.EditorGUI.EndChangeCheck()) 231 | changed = true; 232 | 233 | UnityEngine.GUILayout.Space(UnityEditor.EditorGUIUtility.standardVerticalSpacing); 234 | 235 | UnityEditor.EditorGUIUtility.labelWidth = labelWidth; 236 | 237 | if (changed) 238 | settings.SaveSettings(); 239 | } 240 | #endregion 241 | } 242 | } 243 | } 244 | #endif //Addressables 245 | #endif // UNITY_EDITOR 246 | -------------------------------------------------------------------------------- /Editor/AkAddressablesEditorSettings.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c878185e4dc06f9419335005707a63a6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/AkAddressablesEditorUtilities.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES && UNITY_EDITOR 19 | 20 | using UnityEngine; 21 | using System; 22 | using System.Collections.Generic; 23 | using System.IO; 24 | using System.Linq; 25 | using System.Reflection; 26 | using System.Threading.Tasks; 27 | using UnityEditor; 28 | using System.Xml; 29 | 30 | namespace AK.Wwise.Unity.WwiseAddressables 31 | { 32 | [InitializeOnLoad] 33 | public class AkAddressablesEditorUtilities : MonoBehaviour 34 | { 35 | static AkAddressablesEditorUtilities() 36 | { 37 | WwiseAddressableSoundBank.GetWwisePlatformNameFromBuildTarget = GetWwisePlatformNameFromBuildTarget; 38 | } 39 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 40 | static void RefreshIsJsonFileMissing() 41 | { 42 | WwiseProjectDatabase.SoundBankDirectoryUpdated -= RefreshIsJsonFileMissing; 43 | isJsonFileMissing = false; 44 | } 45 | private static bool isJsonFileMissing = false; 46 | #endif 47 | 48 | public static Dictionary SoundbanksInfo = new Dictionary(); 49 | 50 | public class SoundBankInfo 51 | { 52 | public List streamedFileIds = new List(); 53 | public List events = new List(); 54 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 55 | public bool isUserBank = true; 56 | #endif 57 | } 58 | 59 | public class SoundBankEntry : Dictionary 60 | { 61 | } 62 | 63 | public class PlatformEntry : Dictionary<(string,string), SoundBankEntry> 64 | { 65 | public long lastParseTime; 66 | public Dictionary> eventToSoundBankMap = new Dictionary>(); 67 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 68 | public bool containsInvalidEntry = false; 69 | 70 | public PlatformEntry() 71 | { 72 | WwiseProjectDatabase.SoundBankDirectoryUpdated += ResetInvalidEntry; 73 | } 74 | 75 | private void ResetInvalidEntry() 76 | { 77 | containsInvalidEntry = false; 78 | } 79 | #endif 80 | } 81 | 82 | public static string GetWwisePlatformNameFromBuildTarget(BuildTarget platform) 83 | { 84 | return AkBuildPreprocessor.GetPlatformName(platform); 85 | } 86 | 87 | public static bool IsAutoBank(string assetPath) 88 | { 89 | var banksPath = GetFullSoundbanksPath() + Path.DirectorySeparatorChar; 90 | var assetsFullPath = Path.GetFullPath(assetPath); 91 | 92 | string directoryName = Path.GetDirectoryName(assetsFullPath); 93 | var pathContent = directoryName.Split(Path.DirectorySeparatorChar).ToList(); 94 | return pathContent.Contains("Event"); 95 | } 96 | 97 | public static void ParseAssetPath(string assetPath, out string platform, out string language, out string type) 98 | { 99 | platform = string.Empty; 100 | language = "SFX"; 101 | type = "User"; 102 | 103 | var banksPath = GetFullSoundbanksPath() + Path.DirectorySeparatorChar; 104 | var assetsFullPath = Path.GetFullPath(assetPath); 105 | 106 | // TODO Use Path.RelativePath as soon as Unity uses a .NET version that includes it (i.e 2021.3) 107 | var assetRelPath = assetsFullPath.Replace(banksPath, ""); 108 | 109 | string[] parts = assetRelPath.Split(Path.DirectorySeparatorChar); 110 | platform = parts[0]; 111 | 112 | if (parts.Length > 2) 113 | { 114 | // Asset is stored in a sub-folder; we must identify the purpose of the sub-folder. 115 | if (parts[1] == "Media" || parts[1] == "Bus" || parts[1] == "Event") 116 | { 117 | type = parts[1]; 118 | // Starting with Wwise 2022.1, loose media files are stored in a sub-directory named "Media". 119 | // These themselves can be in localized sub-folders. 120 | if (parts.Length > 3) 121 | { 122 | // The sub-sub folder name is the locale string 123 | language = parts[2]; 124 | } 125 | } 126 | else 127 | { 128 | // Localized bank file; the sub-folder name is the locale string 129 | language = parts[1]; 130 | } 131 | } 132 | } 133 | 134 | public static string GetSoundbanksPath() 135 | { 136 | if (AkWwiseEditorSettings.Instance.GeneratedSoundbanksPath == null) 137 | { 138 | UnityEngine.Debug.LogError("Wwise Addressables: You need to set the GeneratedSoundbankPath in the Wwise Editor settings or assets will not be properly imported."); 139 | return string.Empty; 140 | } 141 | var path = Path.Combine("Assets", AkWwiseEditorSettings.Instance.GeneratedSoundbanksPath); 142 | return path.Replace("\\", "/"); 143 | } 144 | 145 | private static string GetFullSoundbanksPath() 146 | { 147 | if (AkWwiseEditorSettings.Instance.GeneratedSoundbanksPath == null) 148 | { 149 | UnityEngine.Debug.LogError("Wwise Addressables: You need to set the GeneratedSoundbankPath in the Wwise Editor settings or assets will not be properly imported."); 150 | return string.Empty; 151 | } 152 | var path = Path.Combine("Assets", AkWwiseEditorSettings.Instance.GeneratedSoundbanksPath); 153 | return Path.GetFullPath(path); 154 | } 155 | 156 | 157 | public static void ClearSoundbankInfo() 158 | { 159 | SoundbanksInfo.Clear(); 160 | } 161 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 162 | public static void AddSoundBank(string bankName, string bankLanguage, string type, ref PlatformEntry soundBankDict, WwiseSoundBankRef sbInfo) 163 | { 164 | bool isAutoBank = !sbInfo.IsUserBank; 165 | soundBankDict.TryAdd((bankName,type), new SoundBankEntry()); 166 | soundBankDict[(bankName,type)][bankLanguage] = new SoundBankInfo(); 167 | for (int i = 0; i < sbInfo.MediasCount; ++i) 168 | { 169 | if (!soundBankDict[(bankName, type)].ContainsKey(sbInfo.Medias[i].Language)) 170 | { 171 | soundBankDict[(bankName,type)][sbInfo.Medias[i].Language] = new SoundBankInfo(); 172 | } 173 | RecordMediaFile(soundBankDict, (bankName, type), sbInfo.Medias[i].ShortId.ToString(), sbInfo.Medias[i].Language); 174 | 175 | } 176 | for (int i = 0; i < sbInfo.EventsCount; ++i) 177 | { 178 | RecordEvent(soundBankDict, (bankName,type), sbInfo.Language, sbInfo.Events[i].Name); 179 | } 180 | soundBankDict[(bankName,type)][sbInfo.Language].isUserBank = sbInfo.IsUserBank; 181 | } 182 | public static async Task ExecuteUpdate(string platformName, string newBankName, string language, string type) 183 | { 184 | WwiseProjectDatabase.SetCurrentPlatform(platformName); 185 | WwiseProjectDatabase.SetCurrentLanguage(language); 186 | 187 | bool doUpdate = true; 188 | if (!SoundbanksInfo.ContainsKey(platformName)) 189 | { 190 | SoundbanksInfo[platformName] = new PlatformEntry(); 191 | WwisePlatformRef platformInfo = new WwisePlatformRef(platformName); 192 | if (platformInfo.Name == null) 193 | { 194 | WwiseProjectDatabase.Init(AkBasePathGetter.GetWwiseRootOutputPath(), platformName, language); 195 | } 196 | } 197 | if (SoundbanksInfo.ContainsKey(platformName) && SoundbanksInfo[platformName].containsInvalidEntry) 198 | { 199 | doUpdate = false; 200 | } 201 | if (doUpdate) 202 | { 203 | await UpdatePlatformEntry(SoundbanksInfo[platformName], newBankName, platformName, language, type); 204 | } 205 | 206 | return SoundbanksInfo[platformName]; 207 | } 208 | 209 | public static async Task UpdatePlatformEntry(PlatformEntry soundBanks, string newBankName, string platformName, string language, string type) 210 | { 211 | WwiseSoundBankRef sbInfo = new WwiseSoundBankRef(newBankName, type); 212 | if (!sbInfo.IsValid) 213 | { 214 | WwiseProjectDatabase.Init(AkBasePathGetter.GetWwiseRootOutputPath(), platformName, language); 215 | sbInfo = new WwiseSoundBankRef(newBankName, type); 216 | } 217 | if (sbInfo.IsValid) 218 | { 219 | AddSoundBank(newBankName, language, type, ref soundBanks, sbInfo); 220 | } 221 | else 222 | { 223 | soundBanks.containsInvalidEntry = true; 224 | } 225 | soundBanks.lastParseTime = DateTime.Now.Ticks; 226 | } 227 | #endif 228 | 229 | public static void AddSoundBank(string bankName, string bankLanguage, ref PlatformEntry soundBankDict) 230 | { 231 | if (!soundBankDict.ContainsKey((bankName, "User"))) 232 | { 233 | soundBankDict.Add((bankName,"User"), new SoundBankEntry()); 234 | } 235 | soundBankDict[(bankName,"User")][bankLanguage] = new SoundBankInfo(); 236 | } 237 | public static PlatformEntry ExecuteParse(string platformName, string newBankName, string xmlFilename) 238 | { 239 | bool doParse = false; 240 | if (!SoundbanksInfo.ContainsKey(platformName)) 241 | { 242 | doParse = true; 243 | } 244 | else if (SoundbanksInfo.ContainsKey(platformName) && !SoundbanksInfo[platformName].ContainsKey((newBankName,"User"))) 245 | { 246 | doParse = true; 247 | } 248 | else 249 | { 250 | var fileModifiedTime = System.IO.File.GetLastWriteTime(xmlFilename); 251 | if (fileModifiedTime.Ticks > SoundbanksInfo[platformName].lastParseTime) 252 | { 253 | doParse = true; 254 | } 255 | } 256 | 257 | if (doParse) 258 | { 259 | var doc = new System.Xml.XmlDocument(); 260 | PlatformEntry soundBanks; 261 | 262 | try 263 | { 264 | doc.Load(xmlFilename); 265 | } 266 | catch (XmlException e) 267 | { 268 | UnityEngine.Debug.LogError("Exception occurred while parsing SoundBanksInfo.xml. Cannot update project SoundBanks info: " + e); 269 | return null; 270 | } 271 | 272 | XmlElement root = doc.DocumentElement; 273 | if (!Int32.TryParse(root.GetAttribute("SchemaVersion"), out int schemaVersion)) 274 | { 275 | Debug.LogError($"Could not parse SoundbanksInfo.xml for {platformName}. Check {xmlFilename} for possible corruption."); 276 | return null; 277 | } 278 | 279 | if (schemaVersion >= 16) 280 | { 281 | soundBanks = ParseSoundBanksInfoXmlv16(doc); 282 | } 283 | else 284 | { 285 | soundBanks = ParseSoundBanksInfoXmlv15(doc); 286 | } 287 | soundBanks.lastParseTime = DateTime.Now.Ticks; 288 | SoundbanksInfo[platformName] = soundBanks; 289 | } 290 | 291 | if (SoundbanksInfo[platformName].eventToSoundBankMap.Count == 0) 292 | { 293 | Debug.LogWarning($"Could not retrieve event data for {platformName} from SoundbanksInfo.xml. Check {xmlFilename} for possible corruption."); 294 | } 295 | 296 | return SoundbanksInfo[platformName]; 297 | } 298 | 299 | private static PlatformEntry ParseSoundBanksInfoXmlv16(XmlDocument doc) 300 | { 301 | var soundBanks = new PlatformEntry(); 302 | var soundBanksRootNode = doc.GetElementsByTagName("SoundBanks"); 303 | for (var i = 0; i < soundBanksRootNode.Count; i++) 304 | { 305 | var soundBankNodes = soundBanksRootNode[i].SelectNodes("SoundBank"); 306 | for (var j = 0; j < soundBankNodes.Count; j++) 307 | { 308 | var bankName = soundBankNodes[j].SelectSingleNode("ShortName").InnerText; 309 | var language = soundBankNodes[j].Attributes.GetNamedItem("Language").Value; 310 | 311 | AddSoundBank(bankName, language, ref soundBanks); 312 | 313 | if (bankName.Equals("Init")) 314 | { 315 | continue; 316 | } 317 | 318 | // First, record all streamed media contained in this bank. 319 | var mediaRootNode = soundBankNodes[j].SelectSingleNode("Media"); 320 | if (mediaRootNode != null) 321 | { 322 | var fileNodes = mediaRootNode.SelectNodes("File"); 323 | foreach (XmlNode fileNode in fileNodes) 324 | { 325 | RecordMediaFile( 326 | soundBanks, 327 | (bankName,"User"), 328 | fileNode.Attributes["Id"].Value, 329 | fileNode.Attributes["Language"].Value); 330 | } 331 | } 332 | 333 | // Then, record all events contained in the bank 334 | var includedEventsNode = soundBankNodes[j].SelectSingleNode("Events"); 335 | if (includedEventsNode != null) 336 | { 337 | var eventNodes = includedEventsNode.SelectNodes("Event"); 338 | foreach (XmlNode eventNode in eventNodes) 339 | { 340 | RecordEvent(soundBanks, (bankName,"User"), language, eventNode.Attributes["Name"].Value); 341 | } 342 | } 343 | } 344 | } 345 | 346 | return soundBanks; 347 | } 348 | 349 | private static PlatformEntry ParseSoundBanksInfoXmlv15(XmlDocument doc) 350 | { 351 | var soundBanks = new PlatformEntry(); 352 | var soundBanksRootNode = doc.GetElementsByTagName("SoundBanks"); 353 | for (var i = 0; i < soundBanksRootNode.Count; i++) 354 | { 355 | var soundBankNodes = soundBanksRootNode[i].SelectNodes("SoundBank"); 356 | for (var j = 0; j < soundBankNodes.Count; j++) 357 | { 358 | var bankName = soundBankNodes[j].SelectSingleNode("ShortName").InnerText; 359 | var language = soundBankNodes[j].Attributes.GetNamedItem("Language").Value; 360 | 361 | AddSoundBank(bankName, language, ref soundBanks); 362 | 363 | if (bankName.Equals("Init")) 364 | { 365 | continue; 366 | } 367 | 368 | var includedEventsNode = soundBankNodes[j].SelectSingleNode("IncludedEvents"); 369 | if (includedEventsNode != null) 370 | { 371 | var eventNodes = includedEventsNode.SelectNodes("Event"); 372 | for (var e = 0; e < eventNodes.Count; e++) 373 | { 374 | RecordEvent(soundBanks, (bankName,"User"), language, eventNodes[e].Attributes["Name"].Value); 375 | 376 | var streamedFilesRootNode = eventNodes[e].SelectSingleNode("ReferencedStreamedFiles"); 377 | if (streamedFilesRootNode != null) 378 | { 379 | var streamedFileNodes = streamedFilesRootNode.SelectNodes("File"); 380 | if (streamedFileNodes.Count > 0) 381 | { 382 | for (var s = 0; s < streamedFileNodes.Count; s++) 383 | { 384 | RecordMediaFile( 385 | soundBanks, 386 | (bankName,"User"), 387 | streamedFileNodes[s].Attributes["Id"].Value, 388 | streamedFileNodes[s].Attributes.GetNamedItem("Language").Value); 389 | } 390 | } 391 | } 392 | } 393 | } 394 | } 395 | } 396 | 397 | return soundBanks; 398 | } 399 | 400 | public static PlatformEntry GetPlatformSoundbanks(string platformName) 401 | { 402 | return SoundbanksInfo[platformName]; 403 | } 404 | //Parse soundbank xml file to get a dict of the streaming wem files 405 | public static async Task ParsePlatformSoundbanks(string platformName, string newBankName, string language, string type) 406 | { 407 | if (platformName == null) 408 | { 409 | platformName = AkBasePathGetter.GetPlatformName(); 410 | } 411 | 412 | var sourceFolder = Path.Combine("Assets", AkWwiseEditorSettings.Instance.GeneratedSoundbanksPath, platformName); 413 | 414 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 415 | var jsonFilename = Path.Combine(sourceFolder, "SoundbanksInfo.json"); 416 | if (File.Exists(jsonFilename)) 417 | { 418 | return await ExecuteUpdate(platformName, newBankName, language, type); 419 | } 420 | if (!isJsonFileMissing && AkUtilities.IsAutoBankEnabled()) 421 | { 422 | WwiseProjectDatabase.SoundBankDirectoryUpdated += RefreshIsJsonFileMissing; 423 | isJsonFileMissing = true; 424 | Debug.LogWarning($"Could not find SoundbanksInfo.json, falling back to SoundbanksInfo.xml." + 425 | $"Using the SoundbanksInfo.xml is not the recommended option and it involves a manual support for auto-defined Soundbanks." + 426 | $"To benefit from an automatic support of auto-defined Soundbanks, make sure Object GUID, Object Path and Generate JSON Metadata is checked in the WwiseProject. Then, clear {sourceFolder} and regenerate the Soundbanks."); 427 | } 428 | #endif 429 | var xmlFilename = Path.Combine(sourceFolder, "SoundbanksInfo.xml"); 430 | if (!File.Exists(xmlFilename)) 431 | { 432 | Debug.LogWarning($"Could not find SoundbanksInfo.xml at {Path.Combine(AkWwiseEditorSettings.Instance.GeneratedSoundbanksPath, platformName)}. Check the Generated Soundbanks Path in the Unity Wwise project settings. Using the Wwise Project to find SoundbanksInfo.xml."); 433 | if (!AkBasePathGetter.GetSoundBankPaths(platformName, out sourceFolder, out string destinationFolder)) 434 | { 435 | Debug.LogError($"Failed to import {newBankName}. Could not get SoundBank folder for {platformName} from Wwise Project {AkWwiseEditorSettings.Instance.WwiseProjectPath}."); 436 | return null; 437 | } 438 | 439 | xmlFilename = Path.Combine(sourceFolder, "SoundbanksInfo.xml"); 440 | if(!File.Exists(xmlFilename)) 441 | { 442 | Debug.LogError($"Failed to import {newBankName}. Could not find SoundbanksInfo for {platformName} platform. Make sure your SoundBanks are generated and that the setting \"Generate XML Metadata\" is enabled. Then, clear {sourceFolder} and regenerate the Soundbanks."); 443 | return null; 444 | } 445 | } 446 | return ExecuteParse(platformName, newBankName, xmlFilename); 447 | } 448 | 449 | public static void FindAndSetBankReference(WwiseAddressableSoundBank addressableBankAsset, string name) 450 | { 451 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 452 | if (addressableBankAsset.IsAutoBank) 453 | { 454 | WwiseEventReference.FindEventReferenceAndSetAddressableBank(addressableBankAsset, name); 455 | return; 456 | } 457 | #endif 458 | WwiseBankReference.FindBankReferenceAndSetAddressableBank(addressableBankAsset, name); 459 | } 460 | 461 | public static void EnsureInitBankAssetCreated() 462 | { 463 | var guids = UnityEditor.AssetDatabase.FindAssets("t:" + typeof(WwiseInitBankReference).Name, new string[] { AkWwiseEditorSettings.WwiseScriptableObjectRelativePath }); 464 | var InitBankAssetPath = Path.Combine(AkWwiseEditorSettings.WwiseScriptableObjectRelativePath, "InitBank.asset"); 465 | if (guids.Length == 0) 466 | { 467 | try 468 | { 469 | AssetDatabase.StartAssetEditing(); 470 | WwiseInitBankReference InitBankRef = UnityEngine.ScriptableObject.CreateInstance(); 471 | UnityEditor.AssetDatabase.CreateAsset(InitBankRef, InitBankAssetPath); 472 | } 473 | finally 474 | { 475 | AssetDatabase.StopAssetEditing(); 476 | } 477 | } 478 | } 479 | 480 | private static void RecordEvent(PlatformEntry soundBanks, (string,string) bankKey, string language, string eventName) 481 | { 482 | soundBanks[bankKey][language].events.Add(eventName); 483 | } 484 | 485 | private static void RecordMediaFile(PlatformEntry soundBanks, (string,string) bankKey, string id, string language) 486 | { 487 | #if !WWISE_ADDRESSABLES_24_1_OR_LATER 488 | if (!soundBanks[bankKey].ContainsKey(language)) 489 | { 490 | AddSoundBank(bankKey.Item1, language, ref soundBanks); 491 | } 492 | #endif 493 | // Record that this bank "contains" this streamed media file 494 | soundBanks[bankKey][language].streamedFileIds.Add(id); 495 | 496 | // Record that this streamed media file is "contained" in this bank 497 | if (!soundBanks.eventToSoundBankMap.ContainsKey(id)) 498 | { 499 | soundBanks.eventToSoundBankMap[id] = new List<(string,string)>(); 500 | } 501 | soundBanks.eventToSoundBankMap[id].Add(bankKey); 502 | } 503 | } 504 | } 505 | #endif -------------------------------------------------------------------------------- /Editor/AkAddressablesEditorUtilities.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5bae130b19e387942ada5e56b59d5fe0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/BuildScriptWwisePacked.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2024 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using System; 21 | using UnityEditor; 22 | using UnityEditor.AddressableAssets; 23 | using UnityEditor.AddressableAssets.Build.DataBuilders; 24 | using UnityEditor.AddressableAssets.Settings; 25 | using UnityEditor.AddressableAssets.Settings.GroupSchemas; 26 | using UnityEngine; 27 | 28 | namespace AK.Wwise.Unity.WwiseAddressables 29 | { 30 | /* 31 | * The wwise build script ONLY builds the bundles automatically generated groupes (e.g. WwiseData_[platform] and WwiseData_[platform]_InitBank) for the target build platform. 32 | */ 33 | [CreateAssetMenu(fileName = "BuildScriptWwisePacked.asset", menuName = "Addressables/Content Builders/Wwise Build Script")] 34 | public class BuildScriptWwisePacked : BuildScriptPackedMode 35 | { 36 | /// 37 | public override string Name 38 | { 39 | get 40 | { 41 | return "Wwise Build Script"; 42 | } 43 | } 44 | /// 45 | /// 46 | protected override string ProcessGroup(AddressableAssetGroup assetGroup, AddressableAssetsBuildContext aaContext) 47 | { 48 | if (assetGroup == null) 49 | return string.Empty; 50 | 51 | var buildTarget = (BuildTarget) Enum.Parse(typeof(BuildTarget), aaContext.runtimeData.BuildTarget); 52 | IncludePlatformSpecificBundles(buildTarget); 53 | 54 | foreach (var schema in assetGroup.Schemas) 55 | { 56 | var errorString = ProcessGroupSchema(schema, assetGroup, aaContext); 57 | if (!string.IsNullOrEmpty(errorString)) 58 | return errorString; 59 | } 60 | 61 | return string.Empty; 62 | } 63 | 64 | private void IncludePlatformSpecificBundles(UnityEditor.BuildTarget target) 65 | { 66 | var wwisePlatform = AkAddressablesEditorUtilities.GetWwisePlatformNameFromBuildTarget(target); 67 | 68 | var addressableSettings = AddressableAssetSettingsDefaultObject.Settings; 69 | 70 | if (addressableSettings == null) 71 | { 72 | UnityEngine.Debug.LogWarningFormat("[Addressables] settings file not found.\nPlease go to Menu/Window/Asset Management/Addressables/Groups, then click 'Create Addressables Settings' button."); 73 | return; 74 | } 75 | 76 | foreach (var group in addressableSettings.groups) 77 | { 78 | var include = false; 79 | 80 | if (group.Name.Contains("WwiseData")) 81 | { 82 | if (group.Name.Contains(wwisePlatform)) 83 | { 84 | include = true; 85 | } 86 | 87 | var bundleSchema = group.GetSchema(); 88 | if (bundleSchema != null) 89 | bundleSchema.IncludeInBuild = include; 90 | } 91 | } 92 | } 93 | } 94 | } 95 | #endif -------------------------------------------------------------------------------- /Editor/BuildScriptWwisePacked.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 85d735f7d0ef4d5499a3be7ccdde6e01 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Samples.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d966cb49e043f6542a8c28bc530750bf 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Samples/WwiseAssetMetadataPreserver.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using UnityEditor.AddressableAssets; 21 | using UnityEditor.AddressableAssets.Settings; 22 | using System.Collections.Generic; 23 | using UnityEditor; 24 | using UnityEngine; 25 | using System.IO; 26 | 27 | namespace AK.Wwise.Unity.WwiseAddressables 28 | { 29 | /* 30 | This class provides the functionality preserve and restore Wwise addressable asset group and label metadata. 31 | This information is lost when the assets are deleted, and clearing the Wwise addressable asset folder is 32 | the simplest way to repair broken WwiseAddressableSoundBanks. 33 | This class should be seen as an example of how to implement this functionality and not a complete solution. 34 | */ 35 | [InitializeOnLoad] 36 | class WwiseAddressableAssetMetadataPreserver 37 | { 38 | static WwiseAddressableAssetMetadataPreserver() 39 | { 40 | if (AkAddressablesEditorSettings.Instance.UseSampleMetadataPreserver) 41 | { 42 | BindMetadataDelegate(); 43 | } 44 | } 45 | 46 | //Sets the GetAssetMetadataDelegate on the WwiseBankPostProcess AssetPostProcessor (which handles importing of .bnk and .wem files) 47 | public static void BindMetadataDelegate() 48 | { 49 | WwiseBankPostProcess.GetAssetMetadataDelegate += GetMetadata; 50 | } 51 | 52 | //Unset the delegate (when the feature is disabled) 53 | public static void UnbindMetaDataDelegate() 54 | { 55 | WwiseBankPostProcess.GetAssetMetadataDelegate -= GetMetadata; 56 | } 57 | 58 | //Creates a AddressableMetadata asset for each Addressable wwise asset that keeps track of groups and labels. 59 | //Metadata assets are created in their own folder in a hierarchy matching the wwise assets 60 | //The folder hierarchy and asset name are used to match the assets on import 61 | [UnityEditor.MenuItem("Assets/Wwise/Addressables/Serialize addressable asset metadata")] 62 | public static void PreserveAllWwiseAssetMetadata() 63 | { 64 | AddressableAssetSettings addressableSettings = AddressableAssetSettingsDefaultObject.Settings; 65 | foreach (AddressableAssetGroup group in addressableSettings.groups) 66 | { 67 | foreach (AddressableAssetEntry assetEntry in group.entries) 68 | { 69 | if (assetEntry.MainAsset) 70 | { 71 | if (assetEntry.MainAsset.GetType().IsSubclassOf(typeof(WwiseAsset))) 72 | { 73 | CreateMetadataAsset(assetEntry.AssetPath, assetEntry.labels, group.name); 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | [UnityEditor.MenuItem("Assets/Wwise/Addressables/Preserve addressable asset metadata", true)] 81 | public static bool ValidatePreserveAllWwiseAssetMetadata() 82 | { 83 | return AkAddressablesEditorSettings.Instance.UseSampleMetadataPreserver; 84 | } 85 | 86 | //Create the necessary subfolders and the metadata asset 87 | public static void CreateMetadataAsset(string assetPath, HashSet assetLabels, string groupName) 88 | { 89 | string soundbankPath = assetPath.Replace(AkAssetUtilities.GetSoundbanksPath(), ""); 90 | var splitBankPath = soundbankPath.Split('/'); 91 | AddressableMetadata metaDataAsset = ScriptableObject.CreateInstance(); 92 | metaDataAsset.labels = new List(assetLabels); 93 | metaDataAsset.groupName = groupName; 94 | 95 | var rootPath = AkAddressablesEditorSettings.Instance.MetadataPath; 96 | 97 | if (!Directory.Exists(Path.Combine(Application.dataPath, rootPath))) 98 | { 99 | UnityEditor.AssetDatabase.CreateFolder("Assets", rootPath); 100 | } 101 | 102 | for (int i = 1; i < splitBankPath.Length - 1; i++) 103 | { 104 | if (!Directory.Exists(Path.Combine(Application.dataPath, Path.Combine(rootPath, splitBankPath[i])))) 105 | { 106 | AssetDatabase.CreateFolder(Path.Combine("Assets", rootPath), splitBankPath[i]); 107 | } 108 | rootPath = Path.Combine(rootPath, splitBankPath[i]); 109 | } 110 | 111 | string assetMetadataPath = Path.Combine("Assets", rootPath, Path.GetFileNameWithoutExtension(assetPath) + ".asset"); 112 | AddressableMetadata oldAsset = AssetDatabase.LoadAssetAtPath(assetMetadataPath); 113 | if (oldAsset) 114 | { 115 | if (!metaDataAsset.IsDifferent(oldAsset)) 116 | { 117 | return; 118 | } 119 | } 120 | 121 | UnityEditor.AssetDatabase.CreateAsset(metaDataAsset, assetMetadataPath); 122 | } 123 | 124 | //We know where the metadata asset should be located based on its platfrom and language. 125 | public static AddressableMetadata FindMetadataAsset(string assetName, string platformName, string languageName) 126 | { 127 | string MetadataAssetPath; 128 | if (languageName !="SFX") 129 | { 130 | MetadataAssetPath = Path.Combine("Assets", AkAddressablesEditorSettings.Instance.MetadataPath, platformName, languageName, Path.GetFileNameWithoutExtension(assetName) + ".asset"); 131 | } 132 | else 133 | { 134 | MetadataAssetPath = Path.Combine("Assets", AkAddressablesEditorSettings.Instance.MetadataPath, platformName, Path.GetFileNameWithoutExtension(assetName) + ".asset"); 135 | } 136 | return AssetDatabase.LoadAssetAtPath(MetadataAssetPath); 137 | } 138 | 139 | //Called when improting .bnk and .wem files. Will attempt to find an existing metadata object in the project and load it. 140 | public static bool GetMetadata(string assetName, string platformName, string languageName, ref AddressableMetadata metaData ) 141 | { 142 | AddressableMetadata asset = FindMetadataAsset(assetName, platformName, languageName); 143 | if ( asset ) 144 | { 145 | metaData = asset; 146 | return true; 147 | } 148 | return false; 149 | } 150 | } 151 | } 152 | 153 | #endif -------------------------------------------------------------------------------- /Editor/Samples/WwiseAssetMetadataPreserver.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7abb5df6682f6ef4da86a53699655c1c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/SceneUpdater.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2024 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | #if UNITY_EDITOR 18 | using UnityEditor; 19 | using UnityEngine; 20 | using UnityEditor.SceneManagement; 21 | using UnityEngine.SceneManagement; 22 | using System.Collections.Generic; 23 | using System.Linq; 24 | public class SceneUpdater 25 | { 26 | private static bool hierarchyChanged = false; 27 | [UnityEditor.MenuItem("Tools/Reload All Scenes")] 28 | public static void ReloadAllScenes() 29 | { 30 | EditorApplication.hierarchyChanged += OnHierarchyChanged; 31 | EditorApplication.update += Update; 32 | // Start the process 33 | ReloadNextScene(); 34 | } 35 | 36 | private static void OnHierarchyChanged() 37 | { 38 | hierarchyChanged = true; 39 | } 40 | 41 | public static bool SceneIsInPackage(string scenePath) 42 | { 43 | if (string.IsNullOrEmpty(scenePath)) 44 | return false; 45 | 46 | return scenePath.StartsWith("Packages/"); 47 | } 48 | 49 | private static List scenePaths; 50 | private static int currentSceneIndex = 0; 51 | private static bool lastSceneWasInPackage = false; 52 | 53 | private static void ReloadNextScene() 54 | { 55 | if (scenePaths == null) 56 | { 57 | string[] sceneGuids = AssetDatabase.FindAssets("t:Scene"); 58 | scenePaths = sceneGuids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToList(); 59 | } 60 | if (currentSceneIndex < scenePaths.Count) 61 | { 62 | string scenePath = scenePaths[currentSceneIndex]; 63 | if (SceneIsInPackage(scenePath)) 64 | { 65 | lastSceneWasInPackage = true; 66 | } 67 | else 68 | { 69 | Scene scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); 70 | lastSceneWasInPackage = false; 71 | hierarchyChanged = false; 72 | } 73 | } 74 | else 75 | { 76 | EditorApplication.hierarchyChanged -= OnHierarchyChanged; 77 | EditorApplication.update -= Update; 78 | if (UnityEditorInternal.InternalEditorUtility.inBatchMode) 79 | { 80 | EditorApplication.Exit(0); 81 | } 82 | else 83 | { 84 | EditorUtility.DisplayDialog("Reload All Scenes", "All scenes have been reloaded.", "OK"); 85 | } 86 | } 87 | } 88 | 89 | private static void Update() 90 | { 91 | if (hierarchyChanged || lastSceneWasInPackage) 92 | { 93 | if (hierarchyChanged) 94 | { 95 | Scene scene = SceneManager.GetActiveScene(); 96 | EditorSceneManager.SaveScene(scene); 97 | } 98 | currentSceneIndex++; 99 | ReloadNextScene(); 100 | } 101 | } 102 | } 103 | #endif -------------------------------------------------------------------------------- /Editor/SceneUpdater.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 66504e0bed1a72b4f9db701abd4f1b76 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/WwiseAssetImportProcessors.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using System.Collections.Concurrent; 21 | using System.Collections.Generic; 22 | using System.IO; 23 | using System.Linq; 24 | using System.Text; 25 | using System.Threading.Tasks; 26 | using UnityEditor; 27 | using UnityEngine; 28 | using UnityEditor.AddressableAssets; 29 | using UnityEditor.AddressableAssets.Settings; 30 | using UnityEditor.AddressableAssets.Settings.GroupSchemas; 31 | 32 | namespace AK.Wwise.Unity.WwiseAddressables 33 | { 34 | public class WwiseBankPostSaveProcessor : UnityEditor.AssetModificationProcessor 35 | { 36 | //Clear parsed soundbank info after updating assets (force reparse when new bnk and wem assets are added) 37 | static string[] OnWillSaveAssets(string[] paths) 38 | { 39 | bool wwiseAssetsModified = false; 40 | foreach (var item in paths) 41 | { 42 | if (Path.GetExtension(item) == ".bnk") 43 | { 44 | wwiseAssetsModified = true; 45 | break; 46 | } 47 | else if (Path.GetExtension(item) == ".wem") 48 | { 49 | wwiseAssetsModified = true; 50 | break; 51 | } 52 | } 53 | 54 | if (wwiseAssetsModified) 55 | { 56 | AkAddressablesEditorUtilities.ClearSoundbankInfo(); 57 | } 58 | 59 | return paths; 60 | } 61 | } 62 | 63 | public class WwiseBankPostProcess : AssetPostprocessor 64 | { 65 | public delegate bool GetAssetMetadata(string name, string platform, string language, ref AddressableMetadata addressableMetadata); 66 | 67 | //Bind to this delegate to customize grouping or labeling of Wwise bank and media assets 68 | public static GetAssetMetadata GetAssetMetadataDelegate; 69 | 70 | static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) 71 | { 72 | AssetDatabase.SaveAssets(); 73 | AssetDatabase.Refresh(); 74 | UpdateAssetReferences(importedAssets); 75 | RemoveAssetReferences(deletedAssets); 76 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 77 | WarnForInvalidEntry(); 78 | #endif 79 | } 80 | 81 | public static async Task UpdateAssetReferences(string[] assets) 82 | { 83 | HashSet bankAssetsToProcess = new HashSet(); 84 | HashSet streamingAssetsToProcess = new HashSet(); 85 | 86 | foreach (var item in assets) 87 | { 88 | if (Path.GetExtension(item) == ".bnk") 89 | { 90 | bankAssetsToProcess.Add(item); 91 | } 92 | 93 | if (Path.GetExtension(item) == ".wem") 94 | { 95 | streamingAssetsToProcess.Add(item); 96 | } 97 | } 98 | 99 | if (bankAssetsToProcess.Count > 0) 100 | { 101 | ConcurrentDictionary addressableAssetCache = 102 | new ConcurrentDictionary(); 103 | await AddBankReferenceToAddressableBankAsset(addressableAssetCache, bankAssetsToProcess); 104 | AddAssetsToAddressablesGroup(bankAssetsToProcess); 105 | } 106 | 107 | if (streamingAssetsToProcess.Count > 0) 108 | { 109 | await AddStreamedAssetsToBanks(streamingAssetsToProcess); 110 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 111 | AddAssetsToAddressablesGroup(streamingAssetsToProcess); 112 | #endif 113 | } 114 | } 115 | 116 | internal static async Task AddStreamedAssetsToBanks(HashSet streamingAssetsAdded) 117 | { 118 | try 119 | { 120 | foreach (var assetPath in streamingAssetsAdded) 121 | { 122 | string name = Path.GetFileNameWithoutExtension(assetPath); 123 | 124 | string platform; 125 | string language; 126 | string type; 127 | AkAddressablesEditorUtilities.ParseAssetPath(assetPath, out platform, out language, out type); 128 | bool isAutoBank = type != "User"; 129 | 130 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 131 | var soundbankInfos = AkAddressablesEditorUtilities.GetPlatformSoundbanks(platform); 132 | #else 133 | var soundbankInfos = await AkAddressablesEditorUtilities.ParsePlatformSoundbanks(platform, name, language, type); 134 | #endif 135 | 136 | if (soundbankInfos.eventToSoundBankMap.TryGetValue(name, out var bankInfos)) 137 | { 138 | foreach (var bankInfo in bankInfos) 139 | { 140 | string bankName = bankInfo.Item1; 141 | string bankType = bankInfo.Item2; 142 | string bankAssetPath = bankType == "User" ? "" : bankType+"/"; 143 | bankAssetPath += bankName; 144 | string bankAssetDir = Path.GetDirectoryName(assetPath); 145 | string addressableBankAssetDir = AkAssetUtilities.GetSoundbanksPath(); 146 | string addressableBankAssetPath = 147 | System.IO.Path.Combine(addressableBankAssetDir, bankAssetPath + ".asset"); 148 | var bankAsset = 149 | AssetDatabase.LoadAssetAtPath(addressableBankAssetPath); 150 | 151 | if (bankAsset == null) 152 | { 153 | continue; 154 | } 155 | 156 | if (!string.IsNullOrEmpty(platform)) 157 | { 158 | if (!soundbankInfos[(bankName,bankType)].TryGetValue(language, 159 | out AkAddressablesEditorUtilities.SoundBankInfo sbInfo)) 160 | { 161 | if (int.TryParse(language, out int result)) 162 | UnityEngine.Debug.LogError( 163 | "Wwise Unity Addressables: Sub-folders for generated files currently not supported. Please turn off the option in Wwise under Project Settings -> SoundBanks"); 164 | else 165 | UnityEngine.Debug.LogError( 166 | $"Wwise Unity Addressables: Unable to process asset at path {assetPath}: Unrecognized language {language}"); 167 | continue; 168 | } 169 | 170 | List MediaIds = sbInfo.streamedFileIds; 171 | bankAsset.UpdateLocalizationLanguages(platform, soundbankInfos[(bankName,bankType)].Keys.ToList()); 172 | bankAsset.SetStreamingMedia(platform, language, bankAssetDir, MediaIds); 173 | EditorUtility.SetDirty(bankAsset); 174 | } 175 | } 176 | } 177 | else 178 | { 179 | UnityEngine.Debug.LogWarning( 180 | $"Wwise Unity Addressables: Can't find containing SoundBank(s) for event {name}"); 181 | } 182 | } 183 | } 184 | finally 185 | { 186 | AssetDatabase.SaveAssets(); 187 | AssetDatabase.Refresh(); 188 | } 189 | } 190 | public static void RemoveAssetReferences(string[] deletedAssets) 191 | { 192 | HashSet bankAssetsToProcess = new HashSet(); 193 | HashSet streamingAssetsToProcess = new HashSet(); 194 | 195 | foreach (var item in deletedAssets) 196 | { 197 | if (Path.GetExtension(item) == ".bnk") 198 | { 199 | bankAssetsToProcess.Add(item); 200 | } 201 | 202 | if (Path.GetExtension(item) == ".wem") 203 | { 204 | streamingAssetsToProcess.Add(item); 205 | } 206 | } 207 | 208 | if (streamingAssetsToProcess.Count > 0) 209 | { 210 | RemoveStreamedAssetsFromBanks(streamingAssetsToProcess); 211 | RemoveAssetsFromAddressables(streamingAssetsToProcess); 212 | } 213 | 214 | if (bankAssetsToProcess.Count > 0) 215 | { 216 | RemoveBanksFromAddressableSoundbanks(bankAssetsToProcess); 217 | RemoveAssetsFromAddressables(bankAssetsToProcess); 218 | } 219 | } 220 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 221 | public static void WarnForInvalidEntry() 222 | { 223 | foreach (var soundbankInfo in AkAddressablesEditorUtilities.SoundbanksInfo) 224 | { 225 | if (soundbankInfo.Value.containsInvalidEntry) 226 | { 227 | string platform = soundbankInfo.Key; 228 | string destinationBasePath; 229 | string sourceBasePath; 230 | AkBasePathGetter.GetSoundBankPaths(platform, out sourceBasePath, out destinationBasePath); 231 | Debug.LogError($"Invalid entry detected in the Soundbanks information for Platform: {platform}. Please make sure Object GUID and Object Path are checked in the WwiseProject. Then clear {sourceBasePath} and regenerate the Soundbanks."); 232 | } 233 | } 234 | } 235 | #endif 236 | struct CreateAssetEntry 237 | { 238 | public WwiseAddressableSoundBank Asset; 239 | public string Path; 240 | public string Name; 241 | } 242 | 243 | internal static AddressableAssetGroup GetOrAddGroup(AddressableAssetSettings settings, string groupName) 244 | { 245 | AddressableAssetGroup group = settings.groups.Find(x => x.Name == groupName); 246 | if (group == null) 247 | { 248 | group = settings.CreateGroup(groupName, false, false, false, new List { settings.DefaultGroup.Schemas[0] }, typeof(BundledAssetGroupSchema)); 249 | var bundleSchema = group.GetSchema(); 250 | if (bundleSchema != null) 251 | { 252 | bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.Uncompressed; 253 | } 254 | } 255 | 256 | return group; 257 | } 258 | 259 | internal static async Task AddBankReferenceToAddressableBankAsset(ConcurrentDictionary addressableAssetCache, HashSet bankAssetsAdded) 260 | { 261 | List itemsToCreate = new List(); 262 | try 263 | { 264 | 265 | foreach (var bankPath in bankAssetsAdded) 266 | { 267 | string name = Path.GetFileNameWithoutExtension(bankPath); 268 | 269 | string platform; 270 | string language; 271 | string type; 272 | AkAddressablesEditorUtilities.ParseAssetPath(bankPath, out platform, out language, out type); 273 | 274 | string noPlatformAndLanguageBankAssetPath = bankPath.Replace(platform + "/", ""); 275 | noPlatformAndLanguageBankAssetPath = noPlatformAndLanguageBankAssetPath.Replace(language + "/", ""); 276 | string addressableBankAssetPath = Path.ChangeExtension(noPlatformAndLanguageBankAssetPath, ".asset"); 277 | string addressableBankAssetDirectory = Path.GetDirectoryName(noPlatformAndLanguageBankAssetPath); 278 | bool isAutoBank = type != "User"; 279 | 280 | // First find or create AddressableBank asset 281 | WwiseAddressableSoundBank addressableBankAsset = null; 282 | if (!addressableAssetCache.TryGetValue(addressableBankAssetPath, out addressableBankAsset)) 283 | { 284 | var results = AssetDatabase.FindAssets(string.Format("{0} t:{1}", name, nameof(WwiseAddressableSoundBank))); 285 | if (results.Length > 0) 286 | { 287 | foreach (var addressableBankGuid in results) 288 | { 289 | string addressableBankPath = AssetDatabase.GUIDToAssetPath(addressableBankGuid); 290 | if (addressableBankPath == addressableBankAssetPath) 291 | { 292 | addressableBankAsset = AssetDatabase.LoadAssetAtPath(addressableBankPath); 293 | } 294 | } 295 | } 296 | 297 | if (name == "Init") 298 | { 299 | AkAddressablesEditorUtilities.EnsureInitBankAssetCreated(); 300 | } 301 | 302 | if (addressableBankAsset == null) 303 | { 304 | if (!AssetDatabase.IsValidFolder(addressableBankAssetDirectory)) 305 | { 306 | StringBuilder currentPathBuilder = new StringBuilder(); 307 | if (addressableBankAssetDirectory != null) 308 | { 309 | var addressableBankAssetParts = addressableBankAssetDirectory.Split('\\'); 310 | 311 | currentPathBuilder.Append(addressableBankAssetParts[0]); 312 | 313 | for (int i = 1; i < addressableBankAssetParts.Length; ++i) 314 | { 315 | string previousPath = currentPathBuilder.ToString(); 316 | 317 | currentPathBuilder.AppendFormat("/{0}", addressableBankAssetParts[i]); 318 | 319 | string currentPath = currentPathBuilder.ToString(); 320 | 321 | if (!AssetDatabase.IsValidFolder(currentPath)) 322 | { 323 | AssetDatabase.CreateFolder(previousPath, addressableBankAssetParts[i]); 324 | } 325 | } 326 | } 327 | } 328 | addressableBankAsset = ScriptableObject.CreateInstance(); 329 | addressableBankAsset.IsAutoBank = isAutoBank; 330 | itemsToCreate.Add(new CreateAssetEntry { Asset = addressableBankAsset, Path = addressableBankAssetPath, Name = name }); 331 | } 332 | else 333 | { 334 | UpdateAddressableBankReference(addressableBankAsset, name); 335 | } 336 | 337 | addressableAssetCache.AddOrUpdate(addressableBankAssetPath, addressableBankAsset, (key, oldValue) => addressableBankAsset); 338 | } 339 | 340 | if (!string.IsNullOrEmpty(platform)) 341 | { 342 | var soundbankInfos = await AkAddressablesEditorUtilities.ParsePlatformSoundbanks(platform, name, language, type); 343 | if (soundbankInfos.ContainsKey((name,type))) 344 | { 345 | addressableBankAsset.UpdateLocalizationLanguages(platform, soundbankInfos[(name,type)].Keys.ToList()); 346 | addressableBankAsset.AddOrUpdate(platform, language, new AssetReferenceWwiseBankData(AssetDatabase.AssetPathToGUID(bankPath))); 347 | 348 | EditorUtility.SetDirty(addressableBankAsset); 349 | } 350 | else 351 | { 352 | Debug.LogWarning($"Could not update {addressableBankAsset.name} with bank located at {bankPath}"); 353 | } 354 | } 355 | } 356 | } 357 | finally 358 | { 359 | if (itemsToCreate.Count > 0) 360 | { 361 | try 362 | { 363 | AssetDatabase.StartAssetEditing(); 364 | foreach (var entry in itemsToCreate) 365 | { 366 | AssetDatabase.CreateAsset(entry.Asset, entry.Path); 367 | UpdateAddressableBankReference(entry.Asset, entry.Name); 368 | } 369 | } 370 | finally 371 | { 372 | AssetDatabase.StopAssetEditing(); 373 | } 374 | } 375 | 376 | AssetDatabase.SaveAssets(); 377 | AssetDatabase.Refresh(); 378 | } 379 | } 380 | 381 | internal static void UpdateAddressableBankReference(WwiseAddressableSoundBank asset, string bankName ) 382 | { 383 | bool bankScriptableObjectUpdated = false; 384 | if (AkAssetUtilities.AddressableBankUpdated != null) 385 | { 386 | bankScriptableObjectUpdated = AkAssetUtilities.AddressableBankUpdated.GetInvocationList().Select(x => (bool)x.DynamicInvoke(asset, bankName)).Any(v => v); 387 | } 388 | if (!bankScriptableObjectUpdated) 389 | { 390 | if (bankName == WwiseInitBankReference.InitBankName) 391 | { 392 | WwiseInitBankReference.FindInitBankReferenceAndSetAddressableBank(asset, bankName); 393 | } 394 | else 395 | { 396 | AkAddressablesEditorUtilities.FindAndSetBankReference(asset, bankName); 397 | } 398 | } 399 | } 400 | 401 | internal static void RemoveStreamedAssetsFromBanks(HashSet streamingAssetsToRemove) 402 | { 403 | try 404 | { 405 | var foundBanks = AssetDatabase.FindAssets($"t:{typeof(WwiseAddressableSoundBank).Name}"); 406 | var updatedBanks = new List(); 407 | foreach (var assetPath in streamingAssetsToRemove) 408 | { 409 | string platform; 410 | string language; 411 | string type; 412 | AkAddressablesEditorUtilities.ParseAssetPath(assetPath, out platform, out language, out type); 413 | var assetGuid = AssetDatabase.AssetPathToGUID(assetPath); 414 | 415 | foreach (var bankGuid in foundBanks) 416 | { 417 | var bankPath = AssetDatabase.GUIDToAssetPath(bankGuid); 418 | var bank = AssetDatabase.LoadAssetAtPath(bankPath); 419 | if (bank.TryRemoveMedia(platform, language, assetGuid)) 420 | { 421 | EditorUtility.SetDirty(bank); 422 | } 423 | } 424 | } 425 | } 426 | finally 427 | { 428 | AssetDatabase.SaveAssets(); 429 | AssetDatabase.Refresh(); 430 | } 431 | } 432 | 433 | internal static void RemoveBanksFromAddressableSoundbanks(HashSet bankAssetsToRemove) 434 | { 435 | try 436 | { 437 | var foundBanks = AssetDatabase.FindAssets($"t:{typeof(WwiseAddressableSoundBank).Name}"); 438 | var updatedBanks = new List(); 439 | foreach (var assetPath in bankAssetsToRemove) 440 | { 441 | string platform; 442 | string language; 443 | string type; 444 | AkAddressablesEditorUtilities.ParseAssetPath(assetPath, out platform, out language, out type); 445 | var assetGuid = AssetDatabase.AssetPathToGUID(assetPath); 446 | 447 | foreach (var bankGuid in foundBanks) 448 | { 449 | var bankPath = AssetDatabase.GUIDToAssetPath(bankGuid); 450 | var bank = AssetDatabase.LoadAssetAtPath(bankPath); 451 | if (bank.TryRemoveBank(platform, language, assetGuid)) 452 | { 453 | EditorUtility.SetDirty(bank); 454 | break; 455 | } 456 | } 457 | } 458 | } 459 | finally 460 | { 461 | AssetDatabase.SaveAssets(); 462 | AssetDatabase.Refresh(); 463 | } 464 | } 465 | 466 | internal static void AddAssetsToAddressablesGroup(HashSet assetsAdded, string groupName = "") 467 | { 468 | if (AddressableAssetSettingsDefaultObject.Settings == null) 469 | { 470 | AddressableAssetSettingsDefaultObject.Settings = 471 | AddressableAssetSettings.Create(AddressableAssetSettingsDefaultObject.kDefaultConfigFolder, 472 | AddressableAssetSettingsDefaultObject.kDefaultConfigAssetName, true, true); 473 | } 474 | 475 | var settings = AddressableAssetSettingsDefaultObject.Settings; 476 | if (settings == null) 477 | { 478 | Debug.LogWarningFormat("[Addressables] settings file not found.\nPlease go to Menu/Window/Asset Management/Addressables/Groups, then click 'Create Addressables Settings' button."); 479 | return; 480 | } 481 | List groupEntriesModified = new List(); 482 | var parseGroupNames = string.IsNullOrEmpty(groupName); 483 | foreach (var assetPath in assetsAdded) 484 | { 485 | string guid = AssetDatabase.AssetPathToGUID(assetPath); 486 | 487 | string platform; 488 | string language; 489 | string type; 490 | AkAddressablesEditorUtilities.ParseAssetPath(assetPath, out platform, out language, out type); 491 | AddressableMetadata assetMetadata = ScriptableObject.CreateInstance(); 492 | 493 | if (parseGroupNames) 494 | { 495 | if (string.IsNullOrEmpty(platform)) 496 | { 497 | Debug.LogError($"Wwise Addressables import : could not parse platform for {assetPath}. It will not be made addressable."); 498 | continue; 499 | } 500 | if (GetAssetMetadataDelegate != null) 501 | { 502 | if (!GetAssetMetadataDelegate.Invoke(Path.GetFileName(assetPath), platform, language, ref assetMetadata)) 503 | { 504 | // If we can't find a metadata asset use the default group name 505 | assetMetadata.groupName = GetDefaultAddressableGroup(Path.GetFileName(assetPath), platform); 506 | } 507 | } 508 | else 509 | { 510 | assetMetadata.groupName = GetDefaultAddressableGroup(Path.GetFileName(assetPath), platform); 511 | } 512 | } 513 | else 514 | { 515 | assetMetadata.groupName = groupName; 516 | } 517 | 518 | AddressableAssetGroup group = GetOrAddGroup(settings, assetMetadata.groupName); 519 | var groupEntry = settings.CreateOrMoveEntry(guid, group); 520 | if (groupEntry != null) 521 | { 522 | if (assetMetadata.labels.Count >0) 523 | { 524 | foreach (string label in assetMetadata.labels) 525 | { 526 | groupEntry.labels.Add(label); 527 | } 528 | } 529 | groupEntriesModified.Add(groupEntry); 530 | } 531 | } 532 | if (groupEntriesModified.Count > 0) 533 | { 534 | settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, groupEntriesModified, true); 535 | AssetDatabase.SaveAssets(); 536 | } 537 | } 538 | 539 | static string GetDefaultAddressableGroup(string assetName, string platform) 540 | { 541 | string groupName = $"WwiseData_{platform}"; 542 | if (assetName == "Init.bnk") 543 | { 544 | groupName = $"WwiseData_{platform}_InitBank"; 545 | } 546 | return groupName; 547 | } 548 | 549 | internal static void RemoveAssetsFromAddressables(HashSet assetsToRemove) 550 | { 551 | if (AddressableAssetSettingsDefaultObject.Settings == null) 552 | { 553 | return; 554 | } 555 | 556 | var settings = AddressableAssetSettingsDefaultObject.Settings; 557 | if (settings == null) 558 | { 559 | Debug.LogWarningFormat("[Addressables] settings file not found.\nPlease go to Menu/Window/Asset Management/Addressables/Groups, then click 'Create Addressables Settings' button."); 560 | return; 561 | } 562 | 563 | foreach (var assetPath in assetsToRemove) 564 | { 565 | string guid = AssetDatabase.AssetPathToGUID(assetPath); 566 | var assetEntry = settings.FindAssetEntry(guid); 567 | if (assetEntry == null) 568 | { 569 | return; 570 | } 571 | var parentGroup = assetEntry.parentGroup; 572 | settings.RemoveAssetEntry(guid); 573 | } 574 | } 575 | } 576 | } 577 | #endif // AK_WWISE_ADDRESSABLES 578 | -------------------------------------------------------------------------------- /Editor/WwiseAssetImportProcessors.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e47a253c01102734b9eded5034bda5d1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/WwiseBankImporter.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using System.Collections.Generic; 21 | using System.Security.Cryptography; 22 | using System.IO; 23 | using System.Threading.Tasks; 24 | using UnityEngine; 25 | 26 | using UnityEditor.AssetImporters; 27 | 28 | namespace AK.Wwise.Unity.WwiseAddressables 29 | { 30 | [ScriptedImporter(1, "bnk")] 31 | public class WwiseBankImporter : ScriptedImporter 32 | { 33 | public override void OnImportAsset(AssetImportContext ctx) 34 | { 35 | ImportAssetAsync(ctx); 36 | } 37 | 38 | private async Task ImportAssetAsync(AssetImportContext ctx) 39 | { 40 | string assetName = Path.GetFileNameWithoutExtension(ctx.assetPath); 41 | 42 | string platform; 43 | string language; 44 | string type; 45 | AkAddressablesEditorUtilities.ParseAssetPath(ctx.assetPath, out platform, out language, out type); 46 | bool isAutoBank = type != "User"; 47 | 48 | if (platform == null) 49 | { 50 | Debug.LogWarning($"Skipping {ctx.assetPath} as its platform couldn't be determined. Make sure it is placed in the appropriate platform folder."); 51 | return; 52 | } 53 | 54 | var soundbankInfos = await AkAddressablesEditorUtilities.ParsePlatformSoundbanks(platform, assetName, language, type); 55 | 56 | if (soundbankInfos == null) 57 | { 58 | Debug.LogWarning($"Skipping {ctx.assetPath}. SoundbanksInfo.xml could not be parsed."); 59 | return; 60 | } 61 | 62 | if (!soundbankInfos.ContainsKey((assetName,type))) 63 | { 64 | Debug.LogWarning($"Skipping {ctx.assetPath} as it was not parsed in SoundbanksInfo.xml. Perhaps this bank no longer exists in the wwise project?"); 65 | return; 66 | } 67 | WwiseSoundBankAsset dataAsset = ScriptableObject.CreateInstance(); 68 | dataAsset.RawData = File.ReadAllBytes(Path.GetFullPath(ctx.assetPath)); 69 | dataAsset.language = language; 70 | dataAsset.isAutoBank = isAutoBank; 71 | var eventNames = soundbankInfos[(assetName,type)][language].events; 72 | if (language !="SFX" && soundbankInfos[(assetName,type)].ContainsKey("SFX")) 73 | { 74 | eventNames.AddRange(soundbankInfos[(assetName,type)]["SFX"].events); 75 | } 76 | dataAsset.eventNames = eventNames; 77 | byte[] hash = MD5.Create().ComputeHash(dataAsset.RawData); 78 | dataAsset.hash = hash; 79 | ctx.AddObjectToAsset(string.Format("WwiseBank_{0}{1}_{2}", platform, language, assetName), dataAsset); 80 | ctx.SetMainObject(dataAsset); 81 | } 82 | } 83 | } 84 | #endif -------------------------------------------------------------------------------- /Editor/WwiseBankImporter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 010ff325a4f1dc3468c3376f72e23da1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/WwiseStreamingAssetImporter.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using System.Collections; 21 | using System.Collections.Generic; 22 | 23 | using UnityEngine; 24 | using System.IO; 25 | using System.Security.Cryptography; 26 | 27 | using UnityEditor.AssetImporters; 28 | 29 | namespace AK.Wwise.Unity.WwiseAddressables 30 | { 31 | [ScriptedImporter(1, "wem")] 32 | public class WwiseStreamingAssetImporter : ScriptedImporter 33 | { 34 | public override void OnImportAsset(AssetImportContext ctx) 35 | { 36 | string assetName = Path.GetFileNameWithoutExtension(ctx.assetPath); 37 | 38 | string platform; 39 | string language; 40 | string type; 41 | AkAddressablesEditorUtilities.ParseAssetPath(ctx.assetPath, out platform, out language, out type); 42 | 43 | if (platform == null) 44 | { 45 | return; 46 | } 47 | 48 | WwiseStreamingMediaAsset dataAsset = ScriptableObject.CreateInstance(); 49 | dataAsset.RawData = File.ReadAllBytes(Path.GetFullPath(ctx.assetPath)); 50 | byte[] hash = MD5.Create().ComputeHash(dataAsset.RawData); 51 | dataAsset.hash = hash; 52 | dataAsset.language = language; 53 | 54 | ctx.AddObjectToAsset(string.Format("WwiseSteamingMedia_{0}{1}_{2}", platform, language, assetName), dataAsset); 55 | ctx.SetMainObject(dataAsset); 56 | } 57 | } 58 | } 59 | #endif -------------------------------------------------------------------------------- /Editor/WwiseStreamingAssetImporter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b8f9069cf83cff243b0eb88f830dbdbf 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ******************************************************************************** 2 | The content of the files in this repository include portions of the AUDIOKINETIC 3 | Wwise Technology released in source code form as part of the SDK package. 4 | 5 | Commercial License Usage 6 | 7 | Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology 8 | may use these files in accordance with the end user license agreement provided 9 | with the software or, alternatively, in accordance with the terms contained in a 10 | written agreement between you and Audiokinetic Inc. 11 | 12 | Copyright (c) 2025 Audiokinetic Inc. 13 | ******************************************************************************** 14 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dd480b869eef8ff4b84c0381ec17e4e6 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Wwise Addressables 2 | 3 | This package adds support for distributing and loading Wwise assets using the Unity Addressables System. 4 | 5 | For more information about installing and using this package, please consult the [official documentation page](https://www.audiokinetic.com/library/edge/?source=Unity&id=pg_addressables.html). 6 | 7 | For beta users, refer to the documentation provided with the software. 8 | 9 | ## Legal 10 | 11 | Copyright © 2024 Audiokinetic Inc. All rights reserved. 12 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c0adf57793c6104588feb44ef4ef219 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f8c71676ebe776247a0ed5d05ca67b73 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/AK.Wwise.Unity.Addressables.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AK.Wwise.Unity.Addressables", 3 | "rootNamespace": "", 4 | "references": [ 5 | "Unity.Addressables", 6 | "Unity.ResourceManager", 7 | "AK.Wwise.Unity.API", 8 | "AK.Wwise.Unity.ProjectDB" 9 | ], 10 | "includePlatforms": [], 11 | "excludePlatforms": [], 12 | "allowUnsafeCode": false, 13 | "overrideReferences": false, 14 | "precompiledReferences": [], 15 | "autoReferenced": true, 16 | "defineConstraints": [ "!UNITY_SERVER" ], 17 | "versionDefines": [ 18 | { 19 | "name": "com.unity.addressables", 20 | "expression": "1.8", 21 | "define": "UNITY_ADDRESSABLES" 22 | }, 23 | { 24 | "name": "com.audiokinetic.wwise.addressables", 25 | "expression": "1.0.0", 26 | "define": "AK_WWISE_ADDRESSABLES" 27 | } 28 | ], 29 | "noEngineReferences": false 30 | } -------------------------------------------------------------------------------- /Runtime/AK.Wwise.Unity.Addressables.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ed608e52440be241a83e7b07717a618 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/AkAddressableBankManager.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using System; 21 | using System.Collections.Concurrent; 22 | using System.Collections.Generic; 23 | using System.IO; 24 | using System.Linq; 25 | using System.Reflection; 26 | using System.Runtime.InteropServices; 27 | using System.Threading.Tasks; 28 | using UnityEngine.AddressableAssets; 29 | using UnityEngine.ResourceManagement.AsyncOperations; 30 | 31 | #if UNITY_EDITOR 32 | using UnityEditor; 33 | using UnityEngine; 34 | using UnityEngine.SceneManagement; 35 | #endif 36 | 37 | namespace AK.Wwise.Unity.WwiseAddressables 38 | { 39 | public class AkAddressableBankManager 40 | { 41 | public static ConcurrentDictionary<(string, bool), WwiseAddressableSoundBank> m_AddressableBanks = 42 | new ConcurrentDictionary<(string, bool), WwiseAddressableSoundBank>(); 43 | 44 | public static ConcurrentDictionary m_BanksToUnload = 45 | new ConcurrentDictionary(); 46 | 47 | private static ConcurrentDictionary m_EventsToFireOnBankLoad = 48 | new ConcurrentDictionary(); 49 | 50 | private static ConcurrentDictionary m_BankHandles = 51 | new ConcurrentDictionary(); 52 | 53 | public static ConcurrentDictionary BankHandles 54 | { 55 | get 56 | { 57 | return m_BankHandles; 58 | } 59 | } 60 | 61 | private static readonly System.Collections.Generic.List m_BanksToUnloadHandle = 62 | new System.Collections.Generic.List(); 63 | 64 | public const uint INVALID_SOUND_BANK_ID = 0; 65 | 66 | private static WwiseAddressableSoundBank initBank; 67 | public static WwiseAddressableSoundBank InitBank 68 | { 69 | get 70 | { 71 | if (initBank == null) 72 | { 73 | initBank = FindInitBank(); 74 | } 75 | return initBank; 76 | } 77 | } 78 | private static AkAddressableBankManager instance; 79 | public static AkAddressableBankManager Instance 80 | { 81 | get 82 | { 83 | if (instance == null) 84 | { 85 | instance = new AkAddressableBankManager(); 86 | } 87 | return instance; 88 | } 89 | private set { Instance = value; } 90 | } 91 | 92 | public struct BankHandle : IEquatable 93 | { 94 | public WwiseAddressableSoundBank Bank; 95 | public uint SoundBankId; 96 | public bool IgnoreRefCount; 97 | public bool RemoveFromBankDictionary; 98 | public int RefCount; 99 | 100 | public BankHandle(WwiseAddressableSoundBank bank, bool ignoreRefCount = false, bool removeFromBankDictionary = false) 101 | { 102 | Bank = bank; 103 | SoundBankId = bank.SoundbankId; 104 | IgnoreRefCount = ignoreRefCount; 105 | RemoveFromBankDictionary = removeFromBankDictionary; 106 | RefCount = 0; 107 | } 108 | 109 | public void IncRef() 110 | { 111 | if (RefCount == 0) 112 | { 113 | m_BanksToUnloadHandle.Remove(this); 114 | } 115 | RefCount++; 116 | } 117 | 118 | public void DecRef(bool ignoreRefCount) 119 | { 120 | if (RefCount == 0) 121 | { 122 | return; 123 | } 124 | 125 | if (ignoreRefCount) 126 | { 127 | RefCount = 0; 128 | } 129 | else 130 | { 131 | RefCount--; 132 | } 133 | 134 | if (RefCount == 0) 135 | { 136 | m_BanksToUnloadHandle.Add(this); 137 | } 138 | } 139 | 140 | public void UnloadBank() 141 | { 142 | if (!IgnoreRefCount) 143 | { 144 | Bank.refCount = Math.Max(0, Bank.refCount - 1); 145 | if (Bank.refCount != 0) 146 | { 147 | return; 148 | } 149 | } 150 | 151 | if (Bank.loadState == BankLoadState.Loading || Bank.loadState == BankLoadState.WaitingForPrepareEvent) 152 | { 153 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager: {Bank.name} will be unloaded after it is done loading"); 154 | m_BanksToUnload.TryAdd(Bank.name, Bank.name); 155 | return; 156 | } 157 | 158 | if (Bank.loadState == BankLoadState.Unloaded) 159 | { 160 | #if WWISE_2024_OR_LATER 161 | AkUnitySoundEngine.PrepareEvent(AkPreparationType.Preparation_Unload, new string[] { Bank.name }, 1); 162 | #else 163 | AkSoundEngine.PrepareEvent(AkPreparationType.Preparation_Unload, new string[] { Bank.name }, 1); 164 | #endif 165 | UnityEngine.Debug.Log($"Wwise Addressables Bank Manager: {Bank.name} is already unloaded."); 166 | return; 167 | } 168 | 169 | if (Bank.loadState == BankLoadState.Loaded || Bank.loadState == BankLoadState.TimedOut) 170 | { 171 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager: Unloading {Bank.name} sound Bank - Bank ID : {Bank.soundbankId}"); 172 | if (Bank.bankType != 0) 173 | { 174 | #if WWISE_2024_OR_LATER 175 | AkUnitySoundEngine.PrepareEvent(AkPreparationType.Preparation_Unload, new string[] { Bank.name }, 1); 176 | AkUnitySoundEngine.UnloadBank(Bank.soundbankId, System.IntPtr.Zero, Bank.bankType); 177 | #else 178 | AkSoundEngine.PrepareEvent(AkPreparationType.Preparation_Unload, new string[] { Bank.name }, 1); 179 | AkSoundEngine.UnloadBank(Bank.soundbankId, System.IntPtr.Zero, Bank.bankType); 180 | #endif 181 | } 182 | else 183 | { 184 | #if WWISE_2024_OR_LATER 185 | AkUnitySoundEngine.UnloadBank(Bank.soundbankId, System.IntPtr.Zero); 186 | #else 187 | AkSoundEngine.UnloadBank(Bank.soundbankId, System.IntPtr.Zero); 188 | #endif 189 | } 190 | } 191 | 192 | m_BanksToUnload.TryRemove(Bank.name, out _); 193 | Bank.soundbankId = 0; 194 | Bank.refCount = 0; 195 | Bank.loadState = BankLoadState.Unloaded; 196 | 197 | if (RemoveFromBankDictionary) 198 | { 199 | if (!m_AddressableBanks.TryRemove((Bank.name, Bank.isAutoBank), out _)) 200 | { 201 | #if UNITY_EDITOR 202 | // Don't unnecessarily log messages when caused by domain reload 203 | if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode && !UnityEditor.EditorApplication.isPlaying) 204 | { 205 | return; 206 | } 207 | #endif 208 | if (InitBank && Bank.name != InitBank.name) 209 | { 210 | UnityEngine.Debug.LogError($"Wwise Addressable Bank Manager: Unloaded {Bank.name}, but it was not in the list of loaded banks"); 211 | } 212 | } 213 | } 214 | } 215 | 216 | public bool Equals(BankHandle other) 217 | { 218 | return SoundBankId == other.SoundBankId; 219 | } 220 | 221 | public override bool Equals(object obj) 222 | { 223 | return obj is BankHandle other && Equals(other); 224 | } 225 | 226 | public override int GetHashCode() 227 | { 228 | return (int)SoundBankId; 229 | } 230 | } 231 | 232 | private uint? m_wwiseMajorVersion = null; 233 | public uint WwiseMajorVersion 234 | { 235 | get 236 | { 237 | if (m_wwiseMajorVersion == null) 238 | { 239 | #if WWISE_2024_OR_LATER 240 | m_wwiseMajorVersion = AkUnitySoundEngine.GetMajorMinorVersion() >> 16; 241 | #else 242 | m_wwiseMajorVersion = AkSoundEngine.GetMajorMinorVersion() >> 16; 243 | #endif 244 | } 245 | return (uint)m_wwiseMajorVersion; 246 | } 247 | } 248 | 249 | public string WriteableMediaDirectory 250 | { 251 | get 252 | { 253 | return Path.Combine(UnityEngine.Application.persistentDataPath, "Media"); 254 | } 255 | } 256 | 257 | private static WwiseAddressableSoundBank FindInitBank() 258 | { 259 | var foundBank = UnityEngine.MonoBehaviour.FindObjectsOfType(); 260 | if (foundBank.Count() == 0) 261 | { 262 | UnityEngine.Debug.LogError("Wwise Addressables : There is no InitBankHolder in the scene, please add one for Wwise to function properly."); 263 | return null; 264 | } 265 | 266 | if (foundBank.Count() > 1) 267 | { 268 | UnityEngine.Debug.LogError("Wwise Addressables : There is more than one InitBankHolder in the scene, which is not recommended."); 269 | } 270 | 271 | WwiseAddressableSoundBank InitBank = foundBank[0].GetAddressableInitBank(); 272 | if (InitBank == null) 273 | { 274 | UnityEngine.Debug.LogError("Wwise Addressables : The InitBankHolder could not get a valid reference to the Init bank."); 275 | return null; 276 | 277 | } 278 | 279 | return InitBank; 280 | } 281 | 282 | struct EventContainer 283 | { 284 | public string eventName; 285 | public object eventObject; 286 | public string methodName; 287 | public object[] methodArgs; 288 | public Type[] methodArgTypes; 289 | } 290 | 291 | bool InitBankLoaded 292 | { 293 | get { return (InitBank != null && InitBank.loadState == BankLoadState.Loaded); } 294 | } 295 | 296 | public void UnloadAllBanks(bool clearBankDictionary = true) 297 | { 298 | foreach (var bank in m_AddressableBanks.Values) 299 | { 300 | UnloadBank(bank, ignoreRefCount: true, removeFromBankDictionary: false); ; 301 | } 302 | if (clearBankDictionary) 303 | { 304 | m_AddressableBanks.Clear(); 305 | } 306 | } 307 | 308 | public void ReloadAllBanks() 309 | { 310 | var m_banksToReload = new ConcurrentDictionary<(string, bool), WwiseAddressableSoundBank>(m_AddressableBanks); 311 | UnloadAllBanks(); 312 | UnloadInitBank(); 313 | #if WWISE_ADDRESSABLES_23_1_OR_LATER || WWISE_ADDRESSABLES_POST_2023 314 | LoadInitBank(AkWwiseInitializationSettings.Instance.LoadBanksAsynchronously); 315 | #else 316 | LoadInitBank(); 317 | #endif 318 | 319 | 320 | foreach (var bank in m_banksToReload.Values) 321 | { 322 | LoadBank(bank, bank.decodeBank, bank.saveDecodedBank); 323 | } 324 | } 325 | 326 | public void SetLanguageAndReloadLocalizedBanks(string language, bool parseBanks = true) 327 | { 328 | var banksToReload = new List(); 329 | if (parseBanks) 330 | { 331 | foreach (var bank in m_AddressableBanks.Values) 332 | { 333 | if (bank.currentLanguage == "SFX" || bank.currentLanguage == language) 334 | continue; 335 | banksToReload.Add(bank); 336 | } 337 | foreach (var bank in banksToReload) 338 | { 339 | UnloadBank(bank, ignoreRefCount: true, removeFromBankDictionary: true); 340 | } 341 | } 342 | DoUnloadBank(); 343 | UnloadInitBank(); 344 | m_EventsToFireOnBankLoad.Clear(); 345 | #if WWISE_2024_OR_LATER 346 | AkUnitySoundEngine.SetCurrentLanguage(language); 347 | AkUnitySoundEngine.RenderAudio(); 348 | #else 349 | AkSoundEngine.SetCurrentLanguage(language); 350 | AkSoundEngine.RenderAudio(); 351 | #endif 352 | #if WWISE_ADDRESSABLES_23_1_OR_LATER || WWISE_ADDRESSABLES_POST_2023 353 | LoadInitBank(AkWwiseInitializationSettings.Instance.LoadBanksAsynchronously); 354 | #else 355 | LoadInitBank(); 356 | #endif 357 | foreach (var bank in banksToReload) 358 | { 359 | LoadBank(bank, bank.decodeBank, bank.saveDecodedBank); 360 | } 361 | } 362 | 363 | public void LoadInitBank(bool loadAsync = true) 364 | { 365 | if (InitBank != null) 366 | { 367 | LoadBank(InitBank, addToBankDictionary: false, loadAsync: loadAsync); 368 | } 369 | } 370 | 371 | public void UnloadInitBank() 372 | { 373 | if (InitBank != null) 374 | { 375 | BankHandle initBankHandle = new BankHandle(InitBank, ignoreRefCount: true, removeFromBankDictionary: false); 376 | initBankHandle.UnloadBank(); 377 | m_BankHandles.TryRemove(InitBank.name, out var outHandle); 378 | } 379 | } 380 | //Todo : support decoding banks and saving decoded banks 381 | public async Task LoadBank(WwiseAddressableSoundBank bank, bool decodeBank = false, bool saveDecodedBank = false, bool addToBankDictionary = true, bool loadAsync = true) 382 | { 383 | bank.decodeBank = decodeBank; 384 | bank.saveDecodedBank = saveDecodedBank; 385 | if (m_AddressableBanks.ContainsKey((bank.name, bank.isAutoBank))) 386 | { 387 | m_AddressableBanks.TryGetValue((bank.name, bank.isAutoBank), out bank); 388 | } 389 | else if (addToBankDictionary) 390 | { 391 | m_AddressableBanks.TryAdd((bank.name, bank.isAutoBank), bank); 392 | } 393 | 394 | if (bank.loadState == BankLoadState.Unloaded || bank.loadState == BankLoadState.WaitingForInitBankToLoad) 395 | { 396 | if (!InitBankLoaded && bank.name != "Init") 397 | { 398 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager: {bank.name} bank will be loaded after the init bank is loaded"); 399 | bank.loadState = BankLoadState.WaitingForInitBankToLoad; 400 | return; 401 | } 402 | } 403 | if (bank.loadState == BankLoadState.Loading) 404 | { 405 | bank.refCount += 1; 406 | return; 407 | } 408 | 409 | if (bank.loadState == BankLoadState.Loaded) 410 | { 411 | m_BankHandles.TryGetValue(bank.name, out var handle); 412 | handle.IncRef(); 413 | m_BankHandles.AddOrUpdate( 414 | bank.name, 415 | key => new BankHandle(bank), // Add new instance if key does not exist 416 | (key, existingValue) => 417 | { 418 | existingValue.IncRef(); 419 | return existingValue; // Update the existing instance 420 | } 421 | ); 422 | return; 423 | } 424 | 425 | bank.refCount += 1; 426 | bank.loadState = BankLoadState.Loading; 427 | 428 | if (bank.Data == null) 429 | { 430 | UnityEngine.Debug.LogError($"Wwise Addressable Bank Manager : {bank.name} could not be loaded - Bank reference not set"); 431 | m_AddressableBanks.TryRemove((bank.name, bank.isAutoBank), out _); 432 | return; 433 | } 434 | 435 | AssetReferenceWwiseBankData bankData; 436 | if (bank.Data.ContainsKey("SFX")) 437 | { 438 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager: Loading {bank.name} bank"); 439 | bankData = bank.Data["SFX"]; 440 | bank.currentLanguage = "SFX"; 441 | } 442 | else 443 | { 444 | #if WWISE_2024_OR_LATER 445 | var currentLanguage = AkUnitySoundEngine.GetCurrentLanguage(); 446 | #else 447 | var currentLanguage = AkSoundEngine.GetCurrentLanguage(); 448 | #endif 449 | if (bank.Data.ContainsKey(currentLanguage)) 450 | { 451 | bankData = bank.Data[currentLanguage]; 452 | bank.currentLanguage = currentLanguage; 453 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager: Loading {bank.name} - {currentLanguage}"); 454 | } 455 | else 456 | { 457 | UnityEngine.Debug.LogError($"Wwise Addressable Bank Manager: {bank.name} could not be loaded in {currentLanguage} language "); 458 | m_AddressableBanks.TryRemove((bank.name, bank.isAutoBank), out _); 459 | bank.loadState = BankLoadState.Unloaded; 460 | return; 461 | } 462 | } 463 | 464 | if (loadAsync) 465 | { 466 | await LoadBankAsync(bank, bankData, true); 467 | } 468 | else 469 | { 470 | LoadBankAsync(bank, bankData, false); 471 | } 472 | } 473 | 474 | public async Task LoadBankAsync(WwiseAddressableSoundBank bank, AssetReferenceWwiseBankData bankData, bool loadAsync) 475 | { 476 | AsyncOperationHandle asyncHandle = new AsyncOperationHandle(); 477 | WwiseSoundBankAsset soundBankAsset; 478 | if (bankData.OperationHandle.IsValid()) 479 | { 480 | soundBankAsset = (WwiseSoundBankAsset)bankData.Asset; 481 | asyncHandle = bankData.OperationHandle; 482 | } 483 | else 484 | { 485 | asyncHandle = bankData.LoadAssetAsync(); 486 | } 487 | #if UNITY_WEBGL && !UNITY_EDITOR 488 | // On WebGL, we MUST load asynchronously in order to yield back to the browser. 489 | // Failing to do so will result in the thread blocking forever and the asset will never be loaded. 490 | soundBankAsset = await asyncHandle.Task; 491 | #else 492 | if (loadAsync) 493 | { 494 | soundBankAsset = (WwiseSoundBankAsset)await asyncHandle.Task; 495 | } 496 | else 497 | { 498 | soundBankAsset = (WwiseSoundBankAsset)asyncHandle.WaitForCompletion(); 499 | } 500 | #endif 501 | //AsyncHandle gets corrupted in Unity 2021 but properly returns the loaded Asset as expected 502 | #if UNITY_2021_1_OR_NEWER 503 | if (soundBankAsset) 504 | #else 505 | if (asyncHandle.IsValid() && asyncHandle.Status == AsyncOperationStatus.Succeeded) 506 | #endif 507 | { 508 | bank.eventNames = new HashSet(soundBankAsset.eventNames); 509 | var data = soundBankAsset.RawData; 510 | bank.GCHandle = GCHandle.Alloc(data, GCHandleType.Pinned); 511 | 512 | #if WWISE_2024_OR_LATER 513 | var result = AkUnitySoundEngine.LoadBankMemoryCopy(bank.GCHandle.AddrOfPinnedObject(), (uint)data.Length, out uint bankID, out uint bankType); 514 | #else 515 | var result = AkSoundEngine.LoadBankMemoryCopy(bank.GCHandle.AddrOfPinnedObject(), (uint)data.Length, out uint bankID, out uint bankType); 516 | #endif 517 | if (result == AKRESULT.AK_Success) 518 | { 519 | if (m_BankHandles.TryGetValue(bank.name, out var handle)) 520 | { 521 | // Bank already loaded, increment its ref count. 522 | handle.IncRef(); 523 | return; 524 | } 525 | handle = new BankHandle(bank, false, false); 526 | handle.IncRef(); 527 | m_BankHandles.TryAdd(bank.name, handle); 528 | bank.soundbankId = bankID; 529 | bank.bankType = bankType; 530 | //Auto bank will set itself as loaded later 531 | if(!bank.isAutoBank) 532 | { 533 | bank.loadState = BankLoadState.Loaded; 534 | } 535 | else 536 | { 537 | bank.loadState = BankLoadState.WaitingForPrepareEvent; 538 | } 539 | } 540 | else if (result == AKRESULT.AK_BankAlreadyLoaded) 541 | { 542 | bank.loadState = BankLoadState.Loaded; 543 | } 544 | else 545 | { 546 | bank.soundbankId = INVALID_SOUND_BANK_ID; 547 | bank.loadState = BankLoadState.LoadFailed; 548 | if ((int)result == 100) // 100 == AK_InvalidBankType (using the raw int value until this package only supports Wwise 22.1 and up) 549 | { 550 | UnityEngine.Debug.LogWarning($"Wwise Addressable Bank Manager : Bank {bank.name} is an auto-generated bank. The Unity Wwise Addressables package only supports user-defined banks. Avoid using auto-generated banks."); 551 | } 552 | else 553 | { 554 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager : Sound Engine failed to load {bank.name} SoundBank"); 555 | } 556 | } 557 | bank.GCHandle.Free(); 558 | 559 | if (bank.StreamingMedia != null) 560 | { 561 | var assetKeys = new List(); 562 | foreach (var language in bank.StreamingMedia.Keys) 563 | { 564 | foreach (var streamedAsset in bank.StreamingMedia[language].media) 565 | { 566 | if (streamedAsset == null) 567 | { 568 | UnityEngine.Debug.LogError($"Wwise Addressable Bank Manager: Streaming media asset referenced in {bank.name} SoundBank is null"); 569 | continue; 570 | } 571 | assetKeys.Add(streamedAsset); 572 | } 573 | } 574 | if (assetKeys.Count > 0) 575 | { 576 | #if UNITY_EDITOR 577 | if ((EditorSettings.enterPlayModeOptionsEnabled && (EditorSettings.enterPlayModeOptions & EnterPlayModeOptions.DisableDomainReload) != 0) || EditorApplication.isPlaying) 578 | { 579 | var startingSceneName = SceneManager.GetActiveScene().name; 580 | #endif 581 | 582 | var streamingAssetAsyncHandle = Addressables.LoadAssetsAsync(assetKeys.AsEnumerable(), streamingMedia => 583 | { 584 | AkAssetUtilities.UpdateStreamedFileIfNecessary(WriteableMediaDirectory, streamingMedia); 585 | }, Addressables.MergeMode.Union, false); 586 | 587 | await streamingAssetAsyncHandle.Task; 588 | #if UNITY_EDITOR 589 | if (startingSceneName != SceneManager.GetActiveScene().name) 590 | { 591 | bank.loadState = BankLoadState.TimedOut; 592 | } 593 | #endif 594 | Addressables.Release(streamingAssetAsyncHandle); 595 | #if UNITY_EDITOR 596 | } 597 | #endif 598 | } 599 | } 600 | } 601 | else 602 | { 603 | UnityEngine.Debug.LogError($"Wwise Addressable Bank Manager : Failed to load {bank.name} SoundBank"); 604 | bank.loadState = BankLoadState.LoadFailed; 605 | } 606 | 607 | // WG-60155 Release the bank asset AFTER streaming media assets are handled, otherwise Unity can churn needlessly if they are all in the same asset bundle! 608 | if(bank.loadState != BankLoadState.TimedOut) 609 | OnBankLoaded(bank); 610 | if (asyncHandle.IsValid()) 611 | { 612 | Addressables.Release(asyncHandle); 613 | } 614 | } 615 | 616 | public void DoUnloadBank() 617 | { 618 | foreach (var bankToUnload in m_BanksToUnloadHandle) 619 | { 620 | bankToUnload.UnloadBank(); 621 | m_BankHandles.TryRemove(bankToUnload.Bank.name, out var outHandle); 622 | } 623 | 624 | m_BanksToUnloadHandle.Clear(); 625 | } 626 | public void UnloadBank(WwiseAddressableSoundBank bank, bool ignoreRefCount = true, bool removeFromBankDictionary = true) 627 | { 628 | if (m_BankHandles.TryGetValue(bank.name, out var handle)) 629 | { 630 | var handleOriginal = handle; 631 | handle.DecRef(ignoreRefCount); 632 | if (!handle.RemoveFromBankDictionary) 633 | { 634 | handle.RemoveFromBankDictionary = removeFromBankDictionary; 635 | } 636 | m_BankHandles.TryUpdate(bank.name, handle, handleOriginal); 637 | } 638 | } 639 | 640 | public bool LoadedBankContainsEvent(string eventName, uint eventId, object eventObject, string methodName, Type[] methodArgTypes, object[] methodArgs) 641 | { 642 | foreach (var bank in m_AddressableBanks.Values) 643 | { 644 | if (bank.loadState == BankLoadState.Loaded && bank.eventNames != null && bank.eventNames.Contains(eventName)) 645 | { 646 | return true; 647 | } 648 | } 649 | 650 | if (methodName == "ExecuteAction") 651 | { 652 | UnityEngine.Debug.LogWarning($"Wwise Addressables : Trying to execute action on {eventName} but it's soundbank hasn't loaded. Aborting."); 653 | return false; 654 | } 655 | 656 | UnityEngine.Debug.LogWarning($"Wwise Addressables : {eventName} will be delayed, because its soundbank has not been loaded."); 657 | m_EventsToFireOnBankLoad.TryAdd(eventId, new EventContainer { eventName = eventName, eventObject = eventObject, methodName = methodName, methodArgTypes = methodArgTypes, methodArgs = methodArgs }); 658 | return false; 659 | } 660 | 661 | private Type m_AkEventType; 662 | private Type EventType 663 | { 664 | get 665 | { 666 | if (m_AkEventType == null) 667 | { 668 | var assembly = Assembly.Load("AK.Wwise.Unity.API.WwiseTypes"); 669 | m_AkEventType = assembly.GetType("AK.Wwise.Event"); 670 | } 671 | return m_AkEventType; 672 | } 673 | } 674 | 675 | public void OnAutoBankLoaded(WwiseAddressableSoundBank bank) 676 | { 677 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager : Loaded {bank.name} AutoBank - Bank ID : {bank.soundbankId}"); 678 | bank.loadState = BankLoadState.Loaded; 679 | FireEventOnBankLoad(bank, false); 680 | } 681 | 682 | private void FireEventOnBankLoad(WwiseAddressableSoundBank bank, bool skipAutoBank) 683 | { 684 | //Fire any events that were waiting on the bank load 685 | var eventsToRemove = new List(); 686 | foreach (var e in m_EventsToFireOnBankLoad) 687 | { 688 | if (bank.eventNames.Contains(e.Value.eventName)) 689 | { 690 | if (skipAutoBank && bank.isAutoBank) 691 | continue; 692 | 693 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager: Triggering delayed event {e.Value.eventName}"); 694 | MethodInfo handleEvent = EventType.GetMethod(e.Value.methodName, e.Value.methodArgTypes); 695 | handleEvent.Invoke(e.Value.eventObject, e.Value.methodArgs); 696 | eventsToRemove.Add(e.Key); 697 | } 698 | } 699 | 700 | 701 | foreach (var e in eventsToRemove) 702 | { 703 | m_EventsToFireOnBankLoad.TryRemove(e, out _); 704 | } 705 | } 706 | 707 | private void OnBankLoaded(WwiseAddressableSoundBank bank) 708 | { 709 | if (bank.loadState == BankLoadState.Loaded) 710 | { 711 | UnityEngine.Debug.Log($"Wwise Addressable Bank Manager : Loaded {bank.name} bank - Bank ID : {bank.soundbankId}"); 712 | if (InitBankLoaded && bank.name == InitBank.name) 713 | { 714 | foreach (var b in m_AddressableBanks.Values) 715 | { 716 | if (b.loadState == BankLoadState.WaitingForInitBankToLoad) 717 | { 718 | LoadBank(b, b.decodeBank, b.saveDecodedBank); 719 | } 720 | } 721 | } 722 | 723 | FireEventOnBankLoad(bank, true); 724 | } 725 | 726 | else if (bank.loadState == BankLoadState.WaitingForPrepareEvent) 727 | { 728 | bank.BroadcastBankLoaded(); 729 | } 730 | 731 | //Reset bank state if load failed 732 | if (bank.loadState == BankLoadState.LoadFailed) 733 | { 734 | UnloadBank(bank, ignoreRefCount : true); 735 | } 736 | 737 | if (m_BanksToUnload.Keys.Contains(bank.name)) 738 | { 739 | UnloadBank(bank); 740 | } 741 | } 742 | 743 | ~AkAddressableBankManager() 744 | { 745 | #if WWISE_2024_OR_LATER 746 | AkUnitySoundEngine.ClearBanks(); 747 | #else 748 | AkSoundEngine.ClearBanks(); 749 | #endif 750 | } 751 | } 752 | } 753 | 754 | #endif // AK_WWISE_ADDRESSABLES -------------------------------------------------------------------------------- /Runtime/AkAddressableBankManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 409951b9c1e10ac43b2c01e7e97702bb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/AkAddressablesSoundEngineInitialization.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | using System.Collections; 19 | using System.Collections.Generic; 20 | using UnityEngine; 21 | 22 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES && (WWISE_ADDRESSABLES_23_1_OR_LATER || WWISE_ADDRESSABLES_POST_2023) 23 | #if WWISE_2024_OR_LATER 24 | public class AkUnityAddressablesSoundEngineInitialization : AkUnitySoundEngineInitialization 25 | #else 26 | public class AkAddressablesSoundEngineInitialization : AkSoundEngineInitialization 27 | #endif 28 | { 29 | public static void ResetInstance() 30 | { 31 | if(m_Instance != null) 32 | { 33 | InitializationDelegate copyInitialize = m_Instance.initializationDelegate; 34 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 35 | ReInitializationDelegate copyReInitialize = m_Instance.reInitializationDelegate; 36 | #endif 37 | TerminationDelegate copyTerminate = m_Instance.terminationDelegate; 38 | #if WWISE_2024_OR_LATER 39 | m_Instance = new AkUnityAddressablesSoundEngineInitialization(); 40 | #else 41 | m_Instance = new AkAddressablesSoundEngineInitialization(); 42 | #endif 43 | m_Instance.initializationDelegate = copyInitialize; 44 | #if WWISE_ADDRESSABLES_24_1_OR_LATER 45 | m_Instance.reInitializationDelegate = copyReInitialize; 46 | #endif 47 | m_Instance.terminationDelegate = copyTerminate; 48 | } 49 | else 50 | { 51 | #if WWISE_2024_OR_LATER 52 | m_Instance = new AkUnityAddressablesSoundEngineInitialization(); 53 | #else 54 | m_Instance = new AkAddressablesSoundEngineInitialization(); 55 | #endif 56 | } 57 | } 58 | 59 | protected override void LoadInitBank() 60 | { 61 | AK.Wwise.Unity.WwiseAddressables.AkAddressableBankManager.Instance.LoadInitBank(AkWwiseInitializationSettings.Instance.LoadBanksAsynchronously); 62 | } 63 | 64 | protected override void ClearBanks() 65 | { 66 | AK.Wwise.Unity.WwiseAddressables.AkAddressableBankManager.Instance.UnloadAllBanks(clearBankDictionary: false); 67 | AK.Wwise.Unity.WwiseAddressables.AkAddressableBankManager.Instance.UnloadInitBank(); 68 | } 69 | 70 | protected override void ResetBanks() 71 | { 72 | AK.Wwise.Unity.WwiseAddressables.AkAddressableBankManager.Instance.UnloadAllBanks(clearBankDictionary: false); 73 | } 74 | } 75 | #endif -------------------------------------------------------------------------------- /Runtime/AkAddressablesSoundEngineInitialization.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff4f8dbbc8dc4644fbe58cf88e80cf59 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/AkAssetUtilities.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using System.IO; 21 | #if UNITY_EDITOR 22 | using UnityEditor; 23 | using UnityEngine; 24 | using System.Reflection; 25 | #endif 26 | 27 | namespace AK.Wwise.Unity.WwiseAddressables 28 | { 29 | public static class AkAssetUtilities 30 | { 31 | 32 | #if UNITY_EDITOR 33 | public delegate bool AddressableBankCreatedDelegate(WwiseAddressableSoundBank assetRef, string name); 34 | public static AddressableBankCreatedDelegate AddressableBankUpdated; 35 | 36 | public static string GetSoundbanksPath() 37 | { 38 | if (AkWwiseEditorSettings.Instance.GeneratedSoundbanksPath == null) 39 | { 40 | UnityEngine.Debug.LogError("Wwise Addressables: You need to set the GeneratedSoundbankPath in the Wwise Editor settings or assets will not be properly imported."); 41 | return string.Empty; 42 | } 43 | var path = Path.Combine("Assets", AkWwiseEditorSettings.Instance.GeneratedSoundbanksPath); 44 | return path.Replace("\\", "/"); 45 | } 46 | 47 | public static AssetReferenceWwiseAddressableBank GetAddressableBankAssetReference(string name) 48 | { 49 | var assetPath = System.IO.Path.Combine(GetSoundbanksPath(), name + ".asset"); 50 | return new AssetReferenceWwiseAddressableBank(AssetDatabase.AssetPathToGUID(assetPath)); 51 | } 52 | #if !WWISE_ADDRESSABLES_24_1_OR_LATER 53 | public static WwiseAddressableSoundBank GetAddressableBankAsset(string name) 54 | { 55 | //Unity Integration 2023.1 does not support Auto-Banks 56 | return GetAddressableBankAsset(name, false); 57 | } 58 | #endif 59 | 60 | public static WwiseAddressableSoundBank GetAddressableBankAsset(string name, bool IsLookingForAutoBank) 61 | { 62 | var assetPath = System.IO.Path.Combine(GetSoundbanksPath(), name + ".asset"); 63 | if (IsLookingForAutoBank) 64 | { 65 | assetPath = System.IO.Path.Combine(GetSoundbanksPath(), "Event", name + ".asset"); 66 | } 67 | 68 | var asset = AssetDatabase.LoadAssetAtPath(assetPath); 69 | if (asset == null) 70 | { 71 | if (IsLookingForAutoBank) 72 | { 73 | Debug.LogWarning($"Could not find addressable bank asset : {assetPath}. If the event is in an User Defined Soundbank, make sure" + 74 | " to check the \"Is In User Define SoundBank\" box in the editor."); 75 | } 76 | else 77 | { 78 | Debug.LogError($"Could not find addressable bank asset : {assetPath}"); 79 | } 80 | } 81 | 82 | return asset; 83 | } 84 | #endif 85 | public static bool AreHashesEqual(byte[] existingHash, byte[] newHash) 86 | { 87 | if (existingHash == null || newHash == null) 88 | { 89 | return false; 90 | } 91 | 92 | if (existingHash.Length != newHash.Length) 93 | { 94 | return false; 95 | } 96 | 97 | for (int i = 0; i < newHash.Length; i++) 98 | { 99 | if (existingHash[i] != newHash[i]) 100 | { 101 | return false; 102 | } 103 | } 104 | 105 | return true; 106 | } 107 | 108 | public static bool UpdateStreamedFileIfNecessary(string wwiseFolder, WwiseAsset asset) 109 | { 110 | var filePath = Path.Combine(wwiseFolder, asset.GetRelativeFilePath()); 111 | var hashPath = filePath + ".md5"; 112 | if (File.Exists(hashPath)) 113 | { 114 | var existingHash = File.ReadAllBytes(hashPath); 115 | 116 | if (!AreHashesEqual(existingHash, asset.hash)) 117 | { 118 | // Different hash means file content has changed and needs to be updated 119 | WriteFile(filePath, hashPath, asset); 120 | return true; 121 | } 122 | } 123 | else 124 | { 125 | // No hash means we are downloading the file for the first time 126 | WriteFile(filePath, hashPath, asset); 127 | return true; 128 | } 129 | return false; 130 | } 131 | 132 | private static void WriteFile(string filePath, string hashPath, WwiseAsset asset) 133 | { 134 | var destinationDir = Path.GetDirectoryName(filePath); 135 | if (!Directory.Exists(destinationDir)) 136 | { 137 | Directory.CreateDirectory(destinationDir); 138 | } 139 | File.WriteAllBytes(filePath, asset.RawData); 140 | File.WriteAllBytes(hashPath, asset.hash); 141 | } 142 | } 143 | } 144 | #endif // AK_WWISE_ADDRESSABLES 145 | -------------------------------------------------------------------------------- /Runtime/AkAssetUtilities.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e2b2e6f2063987d40adbe971ab82393f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/AkWwiseAddressablesInitializationSettings.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | namespace AK.Wwise.Unity.WwiseAddressables 21 | { 22 | public class AkWwiseAddressablesInitializationSettings : AkWwiseInitializationSettings 23 | { 24 | private static AkWwiseAddressablesInitializationSettings m_Instance = null; 25 | 26 | public new static AkWwiseAddressablesInitializationSettings Instance 27 | { 28 | get 29 | { 30 | if (m_Instance == null) 31 | { 32 | #if WWISE_ADDRESSABLES_POST_2023 || WWISE_ADDRESSABLES_23_1_OR_LATER 33 | #if WWISE_2024_OR_LATER 34 | AkUnityAddressablesSoundEngineInitialization.ResetInstance(); 35 | #else 36 | AkAddressablesSoundEngineInitialization.ResetInstance(); 37 | #endif //WWISE_2024_OR_LATER 38 | #endif //WWISE_ADDRESSABLES_POST_2023 || WWISE_ADDRESSABLES_23_1_OR_LATER 39 | #if UNITY_EDITOR 40 | var name = typeof(AkWwiseInitializationSettings).Name; 41 | var className = typeof(AkWwiseAddressablesInitializationSettings).Name; 42 | m_Instance = ReplaceOrCreateAsset(className, name); 43 | #else 44 | m_Instance = (AkWwiseAddressablesInitializationSettings) CreateInstance(); 45 | UnityEngine.Debug.LogWarning("WwiseUnity: No platform specific settings were created. Default initialization settings will be used."); 46 | #endif 47 | } 48 | 49 | return m_Instance; 50 | } 51 | } 52 | 53 | #if !(WWISE_ADDRESSABLES_POST_2023 || WWISE_ADDRESSABLES_23_1_OR_LATER) 54 | protected override void LoadInitBank() 55 | { 56 | AkAddressableBankManager.Instance.LoadInitBank(); 57 | } 58 | 59 | protected override void ClearBanks() 60 | { 61 | AkAddressableBankManager.Instance.UnloadAllBanks(clearBankDictionary: false); 62 | AkAddressableBankManager.Instance.UnloadInitBank(); 63 | } 64 | #endif 65 | 66 | 67 | #if UNITY_EDITOR 68 | public static AkWwiseAddressablesInitializationSettings ReplaceOrCreateAsset(string className, string fileName) 69 | { 70 | var path = System.IO.Path.Combine(AkWwiseEditorSettings.WwiseScriptableObjectRelativePath, fileName + ".asset"); 71 | var assetExists = string.IsNullOrEmpty(UnityEditor.AssetDatabase.AssetPathToGUID(path)); 72 | if (assetExists) 73 | { 74 | var loadedAsset = UnityEditor.AssetDatabase.LoadAssetAtPath(path); 75 | if (loadedAsset != null) 76 | { 77 | return loadedAsset; 78 | } 79 | else //overwrite current InitializationSettings asset with the addressables one 80 | { 81 | UnityEditor.AssetDatabase.DeleteAsset(path); 82 | var newAsset = CreateInstance(); 83 | UnityEditor.AssetDatabase.CreateAsset(newAsset, path); 84 | return newAsset; 85 | } 86 | } 87 | 88 | var guids = UnityEditor.AssetDatabase.FindAssets("t:" + typeof(AkWwiseAddressablesInitializationSettings).Name); 89 | foreach (var assetGuid in guids) 90 | { 91 | var assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(assetGuid); 92 | var foundAsset = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); 93 | if (foundAsset) 94 | return foundAsset; 95 | } 96 | 97 | var createdAsset = CreateInstance(); 98 | AkUtilities.CreateFolder(AkWwiseEditorSettings.WwiseScriptableObjectRelativePath); 99 | UnityEditor.AssetDatabase.CreateAsset(createdAsset, path); 100 | return createdAsset; 101 | } 102 | #endif 103 | } 104 | } 105 | #endif // AK_WWISE_ADDRESSABLES 106 | -------------------------------------------------------------------------------- /Runtime/AkWwiseAddressablesInitializationSettings.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f444e2f1a81dc54cab58569a7ca372c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/InitBankHolder.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using UnityEngine; 21 | 22 | namespace AK.Wwise.Unity.WwiseAddressables 23 | { 24 | public class InitBankHolder : MonoBehaviour 25 | { 26 | public WwiseInitBankReference InitBank; 27 | 28 | public WwiseAddressableSoundBank GetAddressableInitBank() 29 | { 30 | if (InitBank == null) 31 | { 32 | #if UNITY_EDITOR 33 | var guids = UnityEditor.AssetDatabase.FindAssets("t:" + typeof(WwiseInitBankReference).Name, 34 | new string[] {AkWwiseEditorSettings.WwiseScriptableObjectRelativePath}); 35 | if (guids.Length >0) 36 | { 37 | var assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[0]); 38 | InitBank = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); 39 | if (InitBank) 40 | { 41 | return InitBank.AddressableBank; 42 | } 43 | } 44 | #endif 45 | return null; 46 | } 47 | 48 | return InitBank.AddressableBank; 49 | } 50 | } 51 | } 52 | 53 | #endif //AK_WWISE_ADDRESSABLES -------------------------------------------------------------------------------- /Runtime/InitBankHolder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ed39f2b237e573f4f85990bb5d1440ac 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/WwiseAddressableSoundBank.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Runtime.InteropServices; 24 | using UnityEditor; 25 | using UnityEngine; 26 | using UnityEngine.AddressableAssets; 27 | 28 | namespace AK.Wwise.Unity.WwiseAddressables 29 | { 30 | [Serializable] 31 | public class WwiseAddressableSoundBank : ScriptableObject, ISerializationCallbackReceiver 32 | { 33 | [SerializeField] 34 | internal WwiseBankPerPlatformEntry[] m_dataPerPlatformList; 35 | 36 | [SerializeField] 37 | internal WwiseBankPerPlatformEntry currentPlatformAssets; 38 | 39 | [System.NonSerialized] 40 | internal BankLoadState loadState = BankLoadState.Unloaded; 41 | 42 | [System.NonSerialized] 43 | internal string currentLanguage; 44 | 45 | [System.NonSerialized] 46 | internal uint soundbankId = AkAddressableBankManager.INVALID_SOUND_BANK_ID; 47 | 48 | [System.NonSerialized] 49 | internal GCHandle GCHandle; 50 | 51 | [System.NonSerialized] 52 | internal bool decodeBank; 53 | 54 | [System.NonSerialized] 55 | internal bool saveDecodedBank; 56 | 57 | [System.NonSerialized] 58 | internal HashSet eventNames; 59 | 60 | [System.NonSerialized] 61 | internal int refCount; 62 | 63 | [System.NonSerialized] 64 | internal bool isAutoBank; 65 | 66 | [System.NonSerialized] 67 | internal uint bankType; 68 | 69 | #if UNITY_EDITOR 70 | public delegate string GetWwisePlatformNameDelegate(BuildTarget target); 71 | public static GetWwisePlatformNameDelegate GetWwisePlatformNameFromBuildTarget; 72 | #endif 73 | 74 | public uint SoundbankId 75 | { 76 | get { return soundbankId; } 77 | } 78 | 79 | public BankLoadState LoadState 80 | 81 | { 82 | get { return loadState; } 83 | } 84 | 85 | public bool IsAutoBank 86 | { 87 | get { return isAutoBank; } 88 | set 89 | { 90 | isAutoBank = value; 91 | } 92 | } 93 | 94 | public string CurrentLanguage 95 | { 96 | get { return currentLanguage; } 97 | set 98 | { 99 | currentLanguage = value; 100 | } 101 | } 102 | 103 | public Dictionary Data 104 | { 105 | get 106 | { 107 | #if UNITY_EDITOR 108 | string wwisePlatform = AkBasePathGetter.GetPlatformName(); 109 | return m_dataPerPlatformList.Where(x => x.WwisePlatform == wwisePlatform).Select(x => x.LocalizedBanks).FirstOrDefault(); 110 | #else 111 | return currentPlatformAssets.LocalizedBanks; 112 | #endif 113 | } 114 | } 115 | 116 | public Dictionary StreamingMedia 117 | { 118 | get 119 | { 120 | #if UNITY_EDITOR 121 | string wwisePlatform = AkBasePathGetter.GetPlatformName(); 122 | return m_dataPerPlatformList.Where(x => x.WwisePlatform == wwisePlatform).Select(x => x.LocalizedStreamingMedia).FirstOrDefault(); 123 | #else 124 | return currentPlatformAssets.LocalizedStreamingMedia; 125 | #endif 126 | } 127 | } 128 | 129 | public delegate void BankLoadHandler(); 130 | 131 | public event BankLoadHandler OnBankLoaded; 132 | 133 | public void BroadcastBankLoaded() 134 | { 135 | if (OnBankLoaded != null) 136 | { 137 | OnBankLoaded(); 138 | } 139 | } 140 | 141 | public void OnAfterDeserialize() 142 | { 143 | #if UNITY_EDITOR 144 | if (m_dataPerPlatformList != null) 145 | { 146 | foreach (var entry in m_dataPerPlatformList) 147 | { 148 | entry.Deserialize(); 149 | } 150 | } 151 | #else 152 | currentPlatformAssets.Deserialize(); 153 | #endif 154 | } 155 | 156 | public void OnBeforeSerialize() 157 | { 158 | #if UNITY_EDITOR 159 | 160 | string wwisePlatform = GetWwisePlatformNameFromBuildTarget(EditorUserBuildSettings.activeBuildTarget); 161 | if (m_dataPerPlatformList != null) 162 | { 163 | foreach (var entry in m_dataPerPlatformList) 164 | { 165 | entry.LocalizedBankKeys = entry.LocalizedBanks.Keys.ToArray(); 166 | entry.LocalizedBanksValues = entry.LocalizedBanks.Values.ToArray(); 167 | entry.LocalizedStreamingMediaKeys = entry.LocalizedStreamingMedia.Keys.ToArray(); 168 | entry.LocalizedStreamingMediaValues = new LocalizedStreamingMediaList(); 169 | entry.LocalizedStreamingMediaValues.Add(entry.LocalizedStreamingMedia.Values.ToList()); 170 | 171 | if (entry.WwisePlatform == wwisePlatform) 172 | { 173 | currentPlatformAssets = entry; 174 | } 175 | } 176 | } 177 | #endif 178 | loadState = BankLoadState.Unloaded; 179 | } 180 | 181 | #if UNITY_EDITOR 182 | public void AddOrUpdate(string wwisePlatform, string language, AssetReferenceWwiseBankData bankRef) 183 | { 184 | if (m_dataPerPlatformList == null) 185 | { 186 | m_dataPerPlatformList = Array.Empty(); 187 | } 188 | 189 | WwiseBankPerPlatformEntry foundEntry = null; 190 | foreach (var entry in m_dataPerPlatformList) 191 | { 192 | if (entry.WwisePlatform == wwisePlatform) 193 | { 194 | if (entry.WwisePlatform == wwisePlatform) 195 | { 196 | foundEntry = entry; 197 | break; 198 | } 199 | } 200 | } 201 | 202 | if (foundEntry != null) 203 | { 204 | foundEntry.LocalizedBanks[language] = bankRef; 205 | } 206 | else 207 | { 208 | foundEntry = new WwiseBankPerPlatformEntry { WwisePlatform = wwisePlatform }; 209 | foundEntry.LocalizedBanks[language] = bankRef; 210 | ArrayUtility.Add(ref m_dataPerPlatformList, foundEntry); 211 | } 212 | } 213 | 214 | public void SetStreamingMedia(string wwisePlatform, string language, string platformDir, List streamingMediaIds) 215 | { 216 | List uniqueList = streamingMediaIds.Distinct().ToList(); 217 | foreach (var entry in m_dataPerPlatformList) 218 | { 219 | if (entry.WwisePlatform == wwisePlatform) 220 | { 221 | 222 | entry.LocalizedStreamingMedia[language] = new StreamingMediaList(); 223 | 224 | foreach (var Id in uniqueList) 225 | { 226 | var mediaPath = System.IO.Path.Combine(platformDir, Id + ".wem"); 227 | entry.LocalizedStreamingMedia[language].Add(new AssetReferenceStreamingMedia(AssetDatabase.AssetPathToGUID(mediaPath), Id)); 228 | } 229 | break; 230 | } 231 | } 232 | } 233 | 234 | public void UpdateLocalizationLanguages(string wwisePlatform, List parsedLanguages) 235 | { 236 | if (m_dataPerPlatformList == null) return; 237 | foreach (var entry in m_dataPerPlatformList) 238 | { 239 | if (entry.WwisePlatform == wwisePlatform) 240 | { 241 | var toRemove = new List(); 242 | foreach (var lang in entry.LocalizedStreamingMedia.Keys) 243 | { 244 | if (!parsedLanguages.Contains(lang)) 245 | { 246 | toRemove.Add(lang); 247 | } 248 | } 249 | 250 | foreach (var lang in toRemove) 251 | { 252 | entry.LocalizedStreamingMedia.Remove(lang); 253 | } 254 | 255 | toRemove.Clear(); 256 | foreach (var lang in entry.LocalizedBanks.Keys) 257 | { 258 | if (!parsedLanguages.Contains(lang)) 259 | { 260 | toRemove.Add(lang); 261 | } 262 | } 263 | 264 | foreach (var lang in toRemove) 265 | { 266 | entry.LocalizedBanks.Remove(lang); 267 | } 268 | } 269 | } 270 | } 271 | 272 | public bool TryRemoveMedia(string wwisePlatform, string language, string mediaGuid) 273 | { 274 | foreach (var entry in m_dataPerPlatformList) 275 | { 276 | if (entry.WwisePlatform == wwisePlatform) 277 | { 278 | if (!entry.LocalizedStreamingMedia.ContainsKey(language)) 279 | { 280 | break; 281 | } 282 | AssetReferenceStreamingMedia mediaToRemove = null; 283 | 284 | foreach (var media in entry.LocalizedStreamingMedia[language].media) 285 | { 286 | if (media.AssetGUID == mediaGuid) 287 | { 288 | mediaToRemove = media; 289 | break; 290 | } 291 | } 292 | if (mediaToRemove != null) 293 | { 294 | entry.LocalizedStreamingMedia[language].media.Remove(mediaToRemove); 295 | return true; 296 | } 297 | } 298 | } 299 | return false; 300 | } 301 | 302 | public bool TryRemoveBank(string wwisePlatform, string language, string bankGuid) 303 | { 304 | foreach (var entry in m_dataPerPlatformList) 305 | { 306 | if (entry.WwisePlatform == wwisePlatform) 307 | { 308 | if (!entry.LocalizedBanks.ContainsKey(language)) 309 | { 310 | break; 311 | } 312 | if (entry.LocalizedBanks[language].AssetGUID == bankGuid) 313 | { 314 | entry.LocalizedBanks.Remove(language); 315 | return true; 316 | 317 | } 318 | } 319 | } 320 | return false; 321 | } 322 | #endif 323 | } 324 | 325 | [Serializable] 326 | public class AssetReferenceWwiseAddressableBank : AssetReferenceT 327 | { 328 | public AssetReferenceWwiseAddressableBank(string guid) 329 | : base(guid) 330 | { 331 | } 332 | } 333 | 334 | [Serializable] 335 | public class AssetReferenceWwiseBankData : AssetReferenceT 336 | { 337 | public AssetReferenceWwiseBankData(string guid) 338 | : base(guid) 339 | { 340 | } 341 | } 342 | 343 | [Serializable] 344 | public class AssetReferenceStreamingMedia : AssetReferenceT 345 | { 346 | public string id; 347 | 348 | public AssetReferenceStreamingMedia(string guid, string id) 349 | : base(guid) 350 | { 351 | this.id = id; 352 | } 353 | } 354 | 355 | [Serializable] 356 | public class StreamingMediaList 357 | { 358 | public List media = new List(); 359 | public void Add(AssetReferenceStreamingMedia m) 360 | { 361 | media.Add(m); 362 | } 363 | 364 | 365 | public AssetReferenceStreamingMedia this[int key] 366 | { 367 | get 368 | { 369 | return media[key]; 370 | } 371 | set 372 | { 373 | media[key] = value; 374 | } 375 | } 376 | } 377 | 378 | [Serializable] 379 | public class LocalizedStreamingMediaList 380 | { 381 | public List localizedMediaList = new List(); 382 | public void Add(StreamingMediaList mediaList) 383 | { 384 | localizedMediaList.Add(mediaList); 385 | } 386 | 387 | public void Add(List mediaList) 388 | { 389 | foreach (var l in mediaList) 390 | { 391 | localizedMediaList.Add(l); 392 | } 393 | } 394 | 395 | public StreamingMediaList this[int key] 396 | { 397 | get 398 | { 399 | return localizedMediaList[key]; 400 | } 401 | set 402 | { 403 | localizedMediaList[key] = value; 404 | } 405 | } 406 | } 407 | 408 | [Serializable] 409 | public class WwiseBankPerPlatformEntry 410 | { 411 | public string WwisePlatform; 412 | [NonSerialized] 413 | public Dictionary LocalizedBanks = new Dictionary(); 414 | [NonSerialized] 415 | public Dictionary LocalizedStreamingMedia = new Dictionary(); 416 | 417 | public string[] LocalizedBankKeys; 418 | public AssetReferenceWwiseBankData[] LocalizedBanksValues; 419 | 420 | public string[] LocalizedStreamingMediaKeys; 421 | public LocalizedStreamingMediaList LocalizedStreamingMediaValues; 422 | 423 | public void Deserialize() 424 | { 425 | int idx = 0; 426 | foreach (var key in LocalizedBankKeys) 427 | { 428 | LocalizedBanks.Add(key, LocalizedBanksValues[idx]); 429 | idx++; 430 | } 431 | idx = 0; 432 | foreach (var key in LocalizedStreamingMediaKeys) 433 | { 434 | LocalizedStreamingMedia.Add(key, LocalizedStreamingMediaValues[idx]); 435 | idx++; 436 | } 437 | } 438 | } 439 | 440 | public enum BankLoadState 441 | { 442 | Unloaded, 443 | WaitingForInitBankToLoad, 444 | Loading, 445 | Loaded, 446 | LoadFailed, 447 | TimedOut, 448 | WaitingForPrepareEvent 449 | } 450 | } 451 | #endif // AK_WWISE_ADDRESSABLES 452 | -------------------------------------------------------------------------------- /Runtime/WwiseAddressableSoundBank.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3213ed949058a0d47826c5f3bd819e6d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/WwiseAsset.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | using System.Collections; 19 | using System.Collections.Generic; 20 | using UnityEngine; 21 | 22 | [PreferBinarySerialization] 23 | public abstract class WwiseAsset : ScriptableObject 24 | { 25 | [SerializeField] 26 | [HideInInspector] 27 | public byte[] RawData; 28 | 29 | [SerializeField] 30 | [HideInInspector] 31 | public byte[] hash; 32 | 33 | [SerializeField] 34 | [HideInInspector] 35 | public string language; 36 | 37 | abstract public string GetRelativeFilePath(); 38 | 39 | public int AssetSize 40 | { 41 | get 42 | { 43 | return RawData.Length; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Runtime/WwiseAsset.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fd8810706e62a9e41ab234519642db6d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/WwiseInitBankReference.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | #if AK_WWISE_ADDRESSABLES && UNITY_ADDRESSABLES 19 | 20 | using System.Collections; 21 | using System.Collections.Generic; 22 | using AK.Wwise.Unity.WwiseAddressables; 23 | using UnityEditor; 24 | using UnityEngine; 25 | 26 | namespace AK.Wwise.Unity.WwiseAddressables 27 | { 28 | public class WwiseInitBankReference : ScriptableObject 29 | { 30 | public WwiseAddressableSoundBank AddressableBank; 31 | public const string InitBankName = "Init"; 32 | 33 | #if UNITY_EDITOR 34 | 35 | public void OnEnable() 36 | { 37 | AkAssetUtilities.AddressableBankUpdated += UpdateAddressableBankReference; 38 | } 39 | 40 | public void SetAddressableBank(WwiseAddressableSoundBank asset) 41 | { 42 | if (asset != null) 43 | { 44 | AddressableBank = asset; 45 | EditorUtility.SetDirty(this); 46 | AssetDatabase.SaveAssets(); 47 | AssetDatabase.Refresh(); 48 | } 49 | } 50 | 51 | public bool UpdateAddressableBankReference(WwiseAddressableSoundBank asset, string name) 52 | { 53 | if (this == null) 54 | { 55 | return false; 56 | } 57 | if (name == InitBankName) 58 | { 59 | SetAddressableBank(asset); 60 | return true; 61 | } 62 | 63 | return false; 64 | } 65 | 66 | 67 | public static bool FindInitBankReferenceAndSetAddressableBank(WwiseAddressableSoundBank addressableAsset, 68 | string name) 69 | { 70 | WwiseInitBankReference asset; 71 | var guids = UnityEditor.AssetDatabase.FindAssets("t:" + typeof(WwiseInitBankReference).Name, 72 | new string[] {AkWwiseEditorSettings.WwiseScriptableObjectRelativePath}); 73 | foreach (var assetGuid in guids) 74 | { 75 | var assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(assetGuid); 76 | asset = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); 77 | if (asset) 78 | { 79 | asset.SetAddressableBank(addressableAsset); 80 | } 81 | } 82 | 83 | return guids.Length > 0; 84 | } 85 | 86 | public void OnDestroy() 87 | { 88 | AkAssetUtilities.AddressableBankUpdated -= UpdateAddressableBankReference; 89 | } 90 | #endif 91 | } 92 | } 93 | #endif -------------------------------------------------------------------------------- /Runtime/WwiseInitBankReference.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9289c9e765809ec408877f7c7b8d296d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/WwiseSoundBankAsset.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | using System.Collections.Generic; 19 | using System.IO; 20 | using UnityEngine; 21 | 22 | namespace AK.Wwise.Unity.WwiseAddressables 23 | { 24 | [PreferBinarySerialization] 25 | public class WwiseSoundBankAsset: WwiseAsset 26 | { 27 | [SerializeField] 28 | public List eventNames; 29 | 30 | [SerializeField] public bool isAutoBank; 31 | 32 | public override string GetRelativeFilePath() 33 | { 34 | return language == "SFX" ? name + ".bnk" : Path.Combine(language, name + ".bnk"); 35 | } 36 | 37 | public string GetName() 38 | { 39 | return name; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Runtime/WwiseSoundBankAsset.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 15fd9cd4296478441b466948a802a886 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/WwiseStreamingMediaAsset.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | The content of this file includes portions of the proprietary AUDIOKINETIC Wwise 3 | Technology released in source code form as part of the game integration package. 4 | The content of this file may not be used without valid licenses to the 5 | AUDIOKINETIC Wwise Technology. 6 | Note that the use of the game engine is subject to the Unity(R) Terms of 7 | Service at https://unity3d.com/legal/terms-of-service 8 | 9 | License Usage 10 | 11 | Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use 12 | this file in accordance with the end user license agreement provided with the 13 | software or, alternatively, in accordance with the terms contained 14 | in a written agreement between you and Audiokinetic Inc. 15 | Copyright (c) 2025 Audiokinetic Inc. 16 | *******************************************************************************/ 17 | 18 | using System.IO; 19 | using UnityEngine; 20 | 21 | namespace AK.Wwise.Unity.WwiseAddressables 22 | { 23 | [PreferBinarySerialization] 24 | public class WwiseStreamingMediaAsset : WwiseAsset 25 | { 26 | public override string GetRelativeFilePath() 27 | { 28 | return language == "SFX" ? name + ".wem" : Path.Combine(language, name + ".wem"); 29 | } 30 | 31 | public string GetName() 32 | { 33 | return name; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Runtime/WwiseStreamingMediaAsset.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 649a74834ac549240bc460ca08ac79ca 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.audiokinetic.wwise.addressables", 3 | "version": "2024.1.0", 4 | "displayName": "Wwise Unity Addressables", 5 | "description": "Wwise Unity Addressables", 6 | "unity": "2021.3", 7 | "unityRelease": "32f1", 8 | "dependencies": { 9 | "com.unity.addressables": "1.21.20" 10 | }, 11 | "keywords": [ 12 | "Wwise", 13 | "wwise", 14 | "audiokinetic", 15 | "ak", 16 | "Audiokinetic" 17 | ], 18 | "author": { 19 | "name": "Audiokinetic Inc.", 20 | "email": "info@audiokinetic.com", 21 | "url": "https://www.audiokinetic.com" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82c5872aa1354d24da44c647accf9891 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------