ソースを参照

Add AudioEngineWwise from 25de560e

Signed-off-by: Alex Peterson <[email protected]>
Alex Peterson 1 年間 前
コミット
3b5531571d
100 ファイル変更8163 行追加0 行削除
  1. 5 0
      Gems/AudioEngineWwise/Assets/AudioBank_Dependencies.xml
  2. 14 0
      Gems/AudioEngineWwise/Assets/GameAudioSeedList.seed
  3. 11 0
      Gems/AudioEngineWwise/CMakeLists.txt
  4. 315 0
      Gems/AudioEngineWwise/Code/CMakeLists.txt
  5. 10 0
      Gems/AudioEngineWwise/Code/Platform/Android/AkPlatformFuncs_Platform.h
  6. 24 0
      Gems/AudioEngineWwise/Code/Platform/Android/AudioEngineWwise_Traits_Android.h
  7. 10 0
      Gems/AudioEngineWwise/Code/Platform/Android/AudioEngineWwise_Traits_Platform.h
  8. 36 0
      Gems/AudioEngineWwise/Code/Platform/Android/AudioSystemImpl_wwise_Android.cpp
  9. 10 0
      Gems/AudioEngineWwise/Code/Platform/Android/FileIOHandler_wwise_Platform.h
  10. 9 0
      Gems/AudioEngineWwise/Code/Platform/Android/PAL_android.cmake
  11. 12 0
      Gems/AudioEngineWwise/Code/Platform/Android/platform_android.cmake
  12. 19 0
      Gems/AudioEngineWwise/Code/Platform/Android/platform_android_files.cmake
  13. 10 0
      Gems/AudioEngineWwise/Code/Platform/Common/Default/AkPlatformFuncs_Default.h
  14. 40 0
      Gems/AudioEngineWwise/Code/Platform/Common/Default/FileIOHandler_wwise_Default.cpp
  15. 16 0
      Gems/AudioEngineWwise/Code/Platform/Common/Default/FileIOHandler_wwise_Default.h
  16. 13 0
      Gems/AudioEngineWwise/Code/Platform/Common/MSVC/AkPlatformFuncs_Default.h
  17. 20 0
      Gems/AudioEngineWwise/Code/Platform/Common/Unimplemented/AudioEngineWwise_Unimplemented.cpp
  18. 25 0
      Gems/AudioEngineWwise/Code/Platform/Common/Unimplemented/AudioSystemImpl_wwise_Unimplemented.cpp
  19. 10 0
      Gems/AudioEngineWwise/Code/Platform/Linux/AkPlatformFuncs_Platform.h
  20. 28 0
      Gems/AudioEngineWwise/Code/Platform/Linux/AudioEngineWwise_Traits_Linux.h
  21. 10 0
      Gems/AudioEngineWwise/Code/Platform/Linux/AudioEngineWwise_Traits_Platform.h
  22. 10 0
      Gems/AudioEngineWwise/Code/Platform/Linux/FileIOHandler_wwise_Platform.h
  23. 9 0
      Gems/AudioEngineWwise/Code/Platform/Linux/PAL_linux.cmake
  24. 12 0
      Gems/AudioEngineWwise/Code/Platform/Linux/platform_linux.cmake
  25. 19 0
      Gems/AudioEngineWwise/Code/Platform/Linux/platform_linux_files.cmake
  26. 10 0
      Gems/AudioEngineWwise/Code/Platform/Mac/AkPlatformFuncs_Platform.h
  27. 28 0
      Gems/AudioEngineWwise/Code/Platform/Mac/AudioEngineWwise_Traits_Mac.h
  28. 10 0
      Gems/AudioEngineWwise/Code/Platform/Mac/AudioEngineWwise_Traits_Platform.h
  29. 10 0
      Gems/AudioEngineWwise/Code/Platform/Mac/FileIOHandler_wwise_Platform.h
  30. 9 0
      Gems/AudioEngineWwise/Code/Platform/Mac/PAL_mac.cmake
  31. 18 0
      Gems/AudioEngineWwise/Code/Platform/Mac/platform_mac.cmake
  32. 19 0
      Gems/AudioEngineWwise/Code/Platform/Mac/platform_mac_files.cmake
  33. 10 0
      Gems/AudioEngineWwise/Code/Platform/Windows/AkPlatformFuncs_Platform.h
  34. 10 0
      Gems/AudioEngineWwise/Code/Platform/Windows/AudioEngineWwise_Traits_Platform.h
  35. 29 0
      Gems/AudioEngineWwise/Code/Platform/Windows/AudioEngineWwise_Traits_Windows.h
  36. 28 0
      Gems/AudioEngineWwise/Code/Platform/Windows/AudioSystemImpl_wwise_Windows.cpp
  37. 10 0
      Gems/AudioEngineWwise/Code/Platform/Windows/FileIOHandler_wwise_Platform.h
  38. 9 0
      Gems/AudioEngineWwise/Code/Platform/Windows/PAL_windows.cmake
  39. 8 0
      Gems/AudioEngineWwise/Code/Platform/Windows/platform_windows.cmake
  40. 19 0
      Gems/AudioEngineWwise/Code/Platform/Windows/platform_windows_files.cmake
  41. 10 0
      Gems/AudioEngineWwise/Code/Platform/iOS/AkPlatformFuncs_Platform.h
  42. 10 0
      Gems/AudioEngineWwise/Code/Platform/iOS/AudioEngineWwise_Traits_Platform.h
  43. 24 0
      Gems/AudioEngineWwise/Code/Platform/iOS/AudioEngineWwise_Traits_iOS.h
  44. 10 0
      Gems/AudioEngineWwise/Code/Platform/iOS/FileIOHandler_wwise_Platform.h
  45. 9 0
      Gems/AudioEngineWwise/Code/Platform/iOS/PAL_ios.cmake
  46. 20 0
      Gems/AudioEngineWwise/Code/Platform/iOS/platform_ios.cmake
  47. 19 0
      Gems/AudioEngineWwise/Code/Platform/iOS/platform_ios_files.cmake
  48. 170 0
      Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseGemSystemComponent.cpp
  49. 74 0
      Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseGemSystemComponent.h
  50. 62 0
      Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseModule.cpp
  51. 52 0
      Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderComponent.cpp
  52. 46 0
      Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderComponent.h
  53. 550 0
      Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp
  54. 54 0
      Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.h
  55. 52 0
      Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderComponent.cpp
  56. 46 0
      Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderComponent.h
  57. 314 0
      Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderWorker.cpp
  58. 46 0
      Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderWorker.h
  59. 19 0
      Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemControl_wwise.cpp
  60. 40 0
      Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemControl_wwise.h
  61. 636 0
      Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp
  62. 110 0
      Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h
  63. 185 0
      Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp
  64. 40 0
      Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.h
  65. 20 0
      Gems/AudioEngineWwise/Code/Source/Editor/EditorWwise.qrc
  66. 26 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/auxbus_nor.svg
  67. 26 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/auxbus_nor_hover.svg
  68. 21 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/event_nor.svg
  69. 21 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/event_nor_hover.svg
  70. 12 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/gameparameter_nor.svg
  71. 12 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/gameparameter_nor_hover.svg
  72. 18 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/soundbank_nor.svg
  73. 18 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/soundbank_nor_hover.svg
  74. 18 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/state_nor.svg
  75. 18 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/state_nor_hover.svg
  76. 16 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/stategroup_nor.svg
  77. 16 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/stategroup_nor_hover.svg
  78. 12 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/switch_nor.svg
  79. 12 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/switch_nor_hover.svg
  80. 12 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/switchgroup_nor.svg
  81. 12 0
      Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/switchgroup_nor_hover.svg
  82. 211 0
      Gems/AudioEngineWwise/Code/Source/Engine/ATLEntities_wwise.h
  83. 220 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp
  84. 123 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.h
  85. 77 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputMicrophone.cpp
  86. 30 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputMicrophone.h
  87. 127 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp
  88. 52 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.h
  89. 161 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/WavParser.cpp
  90. 127 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/WavParser.h
  91. 371 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioSourceManager.cpp
  92. 141 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioSourceManager.h
  93. 56 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioSystemImplCVars.cpp
  94. 27 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioSystemImplCVars.h
  95. 2137 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioSystemImpl_wwise.cpp
  96. 195 0
      Gems/AudioEngineWwise/Code/Source/Engine/AudioSystemImpl_wwise.h
  97. 10 0
      Gems/AudioEngineWwise/Code/Source/Engine/Common_wwise.cpp
  98. 113 0
      Gems/AudioEngineWwise/Code/Source/Engine/Common_wwise.h
  99. 106 0
      Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.cpp
  100. 67 0
      Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h

+ 5 - 0
Gems/AudioEngineWwise/Assets/AudioBank_Dependencies.xml

@@ -0,0 +1,5 @@
+<EngineDependencies versionnumber="1.0.0">
+    <Dependency path="sounds/wwise/*.bnk" optional="false" />
+    <Dependency path="libs/gameaudio/wwise/*.xml" optional="false" />
+    <Dependency path=":libs/gameaudio/wwise/levels/*.xml" optional="false" />
+</EngineDependencies>

+ 14 - 0
Gems/AudioEngineWwise/Assets/GameAudioSeedList.seed

@@ -0,0 +1,14 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "AZStd::vector<SeedInfo, allocator>",
+    "ClassData": [
+        {
+            "assetId": {
+                "guid": "{95372958-1CFD-524C-9574-EC7A7BB6F883}"
+            },
+            "platformFlags": 1,
+            "pathHint": "audiobank_dependencies.xml"
+        }
+    ]
+}

+ 11 - 0
Gems/AudioEngineWwise/CMakeLists.txt

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+o3de_gem_setup("AudioEngineWwise")
+
+add_subdirectory(Code)

+ 315 - 0
Gems/AudioEngineWwise/Code/CMakeLists.txt

@@ -0,0 +1,315 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+set(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common)
+
+include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) #for PAL_TRAIT_AUDIO_ENGINE_WWISE Traits
+
+set(AUDIOENGINEWWISE_COMPILEDEFINITIONS
+    $<IF:$<CONFIG:Release>,WWISE_RELEASE,ENABLE_AUDIO_LOGGING>
+)
+
+find_package(Wwise MODULE)
+
+################################################################################
+# Servers
+# (and situations where Wwise SDK is not found or otherwise unavailable)
+################################################################################
+if(NOT PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED OR NOT Wwise_FOUND)
+    # Don't create any Gem targets and aliases.  Nothing should depend on this
+    # Gem directly, because if it doesn't define targets it will cause an error.
+    return()
+endif()
+
+
+################################################################################
+# Clients
+################################################################################
+ly_add_target(
+    NAME ${gem_name}.Private.Object STATIC
+    NAMESPACE Gem
+    FILES_CMAKE
+        audioenginewwise_files.cmake
+        ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
+    PLATFORM_INCLUDE_FILES
+        ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
+    TARGET_PROPERTIES
+        O3DE_PRIVATE_TARGET TRUE
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source/Engine
+            ${pal_dir}
+            ${common_dir}
+    COMPILE_DEFINITIONS
+        PUBLIC
+            ${AUDIOENGINEWWISE_COMPILEDEFINITIONS}
+    BUILD_DEPENDENCIES
+        PUBLIC
+            3rdParty::Wwise
+            Legacy::CryCommon
+            Gem::AudioSystem.API
+)
+
+ly_add_target(
+    NAME ${gem_name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
+    NAMESPACE Gem
+    FILES_CMAKE
+        audioenginewwise_shared_files.cmake
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source/Engine
+            ${pal_dir}
+            ${common_dir}
+    BUILD_DEPENDENCIES
+        PRIVATE
+            Gem::${gem_name}.Private.Object
+)
+
+# Inject the gem name into the Module source file
+ly_add_source_properties(
+    SOURCES
+        Source/AudioEngineWwiseModule.cpp
+    PROPERTY COMPILE_DEFINITIONS
+        VALUES
+            O3DE_GEM_NAME=${gem_name}
+            O3DE_GEM_VERSION=${gem_version})
+
+ly_create_alias(NAME ${gem_name}.Clients NAMESPACE Gem TARGETS Gem::${gem_name})
+ly_create_alias(NAME ${gem_name}.Unified NAMESPACE Gem TARGETS Gem::${gem_name})
+
+################################################################################
+# Tests
+################################################################################
+if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
+   ly_add_target(
+        NAME ${gem_name}.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
+        NAMESPACE Gem
+        FILES_CMAKE
+            audioenginewwise_tests_files.cmake
+            audioenginewwise_shared_files.cmake
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Tests
+                Source/Engine
+                ${pal_dir}
+                ${common_dir}
+        BUILD_DEPENDENCIES
+            PUBLIC
+                Gem::AudioSystem.API
+            PRIVATE
+                AZ::AzTest
+                Gem::${gem_name}.Private.Object
+    )
+
+    ly_add_googletest(
+        NAME Gem::${gem_name}.Tests
+    )
+
+    list(APPEND testTargets ${gem_name}.Tests)
+
+    ly_add_target_files(
+        TARGETS
+            ${testTargets}
+        FILES
+            ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/soundbanks/init.bnk
+        OUTPUT_SUBDIRECTORY
+            Test.Assets/Gems/AudioEngineWwise/sounds/wwise/soundbanks
+    )
+
+endif() # PAL_TRAIT_BUILD_TESTS_SUPPORTED
+
+
+################################################################################
+# Tools / Builders
+################################################################################
+if (PAL_TRAIT_BUILD_HOST_TOOLS)
+    ly_add_target(
+        NAME ${gem_name}.Editor.Private.Object STATIC
+        NAMESPACE Gem
+        AUTORCC
+        FILES_CMAKE
+            audioenginewwise_editor_files.cmake
+        TARGET_PROPERTIES
+            O3DE_PRIVATE_TARGET TRUE
+        COMPILE_DEFINITIONS
+            PUBLIC
+                ${AUDIOENGINEWWISE_COMPILEDEFINITIONS}
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Source/Engine
+                Source/Editor
+                ${pal_dir}
+        BUILD_DEPENDENCIES
+            PUBLIC
+                3rdParty::Wwise
+                Gem::AudioSystem.Editor.API
+                Legacy::CryCommon
+            PRIVATE
+                Legacy::EditorCore
+                Gem::${gem_name}.Private.Object
+        RUNTIME_DEPENDENCIES
+            Gem::AudioSystem.Editor
+    )
+
+    ly_add_target(
+        NAME ${gem_name}.Editor GEM_MODULE
+        NAMESPACE Gem
+        FILES_CMAKE
+            audioenginewwise_editor_shared_files.cmake
+        COMPILE_DEFINITIONS
+            PRIVATE
+                AUDIO_ENGINE_WWISE_EDITOR
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Source/Engine
+                Source/Editor
+                ${pal_dir}
+        BUILD_DEPENDENCIES
+            PUBLIC
+                Gem::AudioSystem.API
+            PRIVATE
+                Gem::${gem_name}.Editor.Private.Object
+        RUNTIME_DEPENDENCIES
+            Gem::AudioSystem.Editor
+    )
+
+    ly_add_target(
+        NAME ${gem_name}.Builder.Private.Object STATIC
+        NAMESPACE Gem
+        FILES_CMAKE
+            audioenginewwise_builder_files.cmake
+        TARGET_PROPERTIES
+            O3DE_PRIVATE_TARGET TRUE
+        COMPILE_DEFINITIONS
+            PUBLIC
+                ${AUDIOENGINEWWISE_COMPILEDEFINITIONS}
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Source/Builder
+                Source/Engine
+                ${pal_dir}
+        BUILD_DEPENDENCIES
+            PUBLIC
+                3rdParty::Wwise
+                AZ::AssetBuilderSDK
+                Gem::AudioSystem.API
+            PRIVATE
+                Legacy::EditorCore
+    )
+
+    ly_add_target(
+        NAME ${gem_name}.Builder GEM_MODULE
+        NAMESPACE Gem
+        FILES_CMAKE
+            audioenginewwise_builder_shared_files.cmake
+        COMPILE_DEFINITIONS
+            PRIVATE
+                AUDIO_ENGINE_WWISE_BUILDER
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Source/Builder
+                Source/Editor
+                Source/Engine
+                ${pal_dir}
+        BUILD_DEPENDENCIES
+            PUBLIC
+                Gem::AudioSystem.API
+                Gem::AudioSystem.Editor.API
+                Legacy::CryCommon
+            PRIVATE
+                Gem::${gem_name}.Builder.Private.Object
+                Gem::${gem_name}.Private.Object
+    )
+
+    ly_create_alias(NAME ${gem_name}.Builders   NAMESPACE Gem TARGETS Gem::${gem_name}.Builder)
+    ly_create_alias(NAME ${gem_name}.Tools      NAMESPACE Gem TARGETS Gem::${gem_name}.Editor)
+
+    if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
+        ly_add_target(
+            NAME ${gem_name}.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
+            NAMESPACE Gem
+            FILES_CMAKE
+                audioenginewwise_editor_tests_files.cmake
+                audioenginewwise_editor_shared_files.cmake
+            INCLUDE_DIRECTORIES
+                PRIVATE
+                    Tests
+                    Source/Builder
+                    Source/Editor
+                    Source/Engine
+                    ${pal_dir}
+            BUILD_DEPENDENCIES
+                PUBLIC
+                    Gem::AudioSystem.API
+                PRIVATE
+                    AZ::AzTest
+                    Gem::${gem_name}.Editor.Private.Object
+                    Gem::${gem_name}.Builder.Private.Object
+                    Legacy::CryCommon
+        )
+
+        ly_add_googletest(
+            NAME Gem::${gem_name}.Editor.Tests
+        )
+
+        list(APPEND editorTestTargets ${gem_name}.Editor.Tests)
+
+        ly_add_target_files(
+            TARGETS
+                ${editorTestTargets}
+            FILES
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/EmptyControl.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/MissingPreloads.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/MissingWwiseFileNode.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/MultiplePreloadsMultipleBanks.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/MultiplePreloadsOneBank.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/OnePreloadMultipleBanks.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/OnePreloadOneBank.xml
+            OUTPUT_SUBDIRECTORY
+                Test.Assets/Gems/AudioEngineWwise/AudioControls
+        )
+
+        ly_add_target_files(
+            TARGETS
+                ${editorTestTargets}
+            FILES
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/MissingAtlPlatformsNode.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/MissingConfigGroupNameAttribute.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/MissingPlatformNameAttributeOnePreload.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/MissingPlatformNode.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/MissingWwiseFileNode.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/MultiplePreloadsMultipleBanks.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/MultiplePreloadsOneBank.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/NoConfigGroups.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/OnePreloadMultipleBanks.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/OnePreloadOneBank.xml
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/AudioControls/Legacy/WrongConfigGroup.xml
+            OUTPUT_SUBDIRECTORY
+                Test.Assets/Gems/AudioEngineWwise/AudioControls/Legacy
+        )
+
+        ly_add_target_files(
+            TARGETS
+                ${editorTestTargets}
+            FILES
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank1.bankdeps
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank2.bankdeps
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank3.bankdeps
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank4.bankdeps
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank5.bankdeps
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank6.bankdeps
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank7.bankdeps
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank8.bankdeps
+                ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Sounds/wwise/test_bank9.bankdeps
+            OUTPUT_SUBDIRECTORY
+                Test.Assets/Gems/AudioEngineWwise/sounds/wwise
+        )
+
+    endif() # PAL_TRAIT_BUILD_TESTS_SUPPORTED
+endif() # PAL_TRAIT_BUILD_HOST_TOOLS

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Android/AkPlatformFuncs_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/AkPlatformFuncs_Default.h>

+ 24 - 0
Gems/AudioEngineWwise/Code/Platform/Android/AudioEngineWwise_Traits_Android.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#define AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND 1
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE 256 /* 256 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "256 (256 KiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_DEFAULT_SPEAKER_CONFIGURATION 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_FILEIO_AKDEVICE_THREAD_AFFINITY_MASK 0
+#if !defined(WWISE_RELEASE)
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE 64 /* 64 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "64 (64 KiB)"
+#endif
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE 32 << 10 /* 32 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "32768 (32 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE_DEFAULT_TEXT "0 (0 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE 2 << 10 /* 2 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE_DEFAULT_TEXT "2048 (2 MiB)"

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Android/AudioEngineWwise_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AudioEngineWwise_Traits_Android.h>

+ 36 - 0
Gems/AudioEngineWwise/Code/Platform/Android/AudioSystemImpl_wwise_Android.cpp

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioSystemImpl_wwise.h>
+
+#include <AK/SoundEngine/Common/AkSoundEngine.h>
+#include <AzCore/Android/AndroidEnv.h>
+
+namespace Audio
+{
+    namespace Platform
+    {
+        void InitializeMemory()
+        {
+        }
+
+        void SetupAkSoundEngine(AkPlatformInitSettings& platformInitSettings)
+        {
+            JNIEnv* jniEnv = AZ::Android::AndroidEnv::Get()->GetJniEnv();
+            jobject javaActivity = AZ::Android::AndroidEnv::Get()->GetActivityRef();
+            JavaVM* javaVM = nullptr;
+            if (jniEnv)
+            {
+               jniEnv->GetJavaVM(&javaVM);
+            }
+
+            platformInitSettings.pJavaVM = javaVM;
+            platformInitSettings.jActivity = javaActivity;
+        }
+    }
+}

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Android/FileIOHandler_wwise_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/FileIOHandler_wwise_Default.h>

+ 9 - 0
Gems/AudioEngineWwise/Code/Platform/Android/PAL_android.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE)

+ 12 - 0
Gems/AudioEngineWwise/Code/Platform/Android/platform_android.cmake

@@ -0,0 +1,12 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(LY_BUILD_DEPENDENCIES
+    PUBLIC
+        OpenSLES
+)

+ 19 - 0
Gems/AudioEngineWwise/Code/Platform/Android/platform_android_files.cmake

@@ -0,0 +1,19 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    ../Common/Default/AkPlatformFuncs_Default.h
+    ../Common/Default/FileIOHandler_wwise_Default.cpp
+    ../Common/Default/FileIOHandler_wwise_Default.h
+    ../Common/Unimplemented/AudioEngineWwise_Unimplemented.cpp
+    AkPlatformFuncs_Platform.h
+    AudioSystemImpl_wwise_Android.cpp
+    AudioEngineWwise_Traits_Android.h
+    AudioEngineWwise_Traits_Platform.h
+    FileIOHandler_wwise_Platform.h
+)

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Common/Default/AkPlatformFuncs_Default.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AK/Tools/Common/AkPlatformFuncs.h>

+ 40 - 0
Gems/AudioEngineWwise/Code/Platform/Common/Default/FileIOHandler_wwise_Default.cpp

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <FileIOHandler_wwise.h>
+#include <AzCore/IO/FileIO.h>
+
+#include <IAudioInterfacesCommonData.h>
+#include <AK/Tools/Common/AkPlatformFuncs.h>
+#include <AudioEngineWwise_Traits_Platform.h>
+
+
+namespace Audio::Platform
+{
+    AkFileHandle GetAkFileHandle(AZ::IO::HandleType realFileHandle)
+    {
+        return (AkFileHandle)static_cast<uintptr_t>(realFileHandle);
+    }
+
+    AZ::IO::HandleType GetRealFileHandle(AkFileHandle akFileHandle)
+    {
+        // On 64 bit systems, strict compilers throw an error trying to reinterpret_cast
+        // from AkFileHandle (a 64 bit pointer) to AZ::IO::HandleType (a uint32_t) because:
+        //
+        // cast from pointer to smaller type 'AZ::IO::HandleType' (aka 'unsigned int') loses information
+        //
+        // However, this is safe because AkFileHandle is a "blind" type that serves as a token.
+        // We create the token and hand it off, and it is handed back whenever file IO is done.
+        return static_cast<AZ::IO::HandleType>((uintptr_t)akFileHandle);
+    }
+
+    void SetThreadProperties(AkThreadProperties& threadProperties)
+    {
+        threadProperties.dwAffinityMask = AZ_TRAIT_AUDIOENGINEWWISE_FILEIO_AKDEVICE_THREAD_AFFINITY_MASK;
+    }
+} // namespace Audio::Platform

+ 16 - 0
Gems/AudioEngineWwise/Code/Platform/Common/Default/FileIOHandler_wwise_Default.h

@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AK/AkPlatforms.h>
+
+
+namespace Audio
+{
+    const static AkFileHandle InvalidAkFileHandle = AkFileHandle((uintptr_t)-1);
+} // namespace Audio

+ 13 - 0
Gems/AudioEngineWwise/Code/Platform/Common/MSVC/AkPlatformFuncs_Default.h

@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#if !defined(NOMINMAX)
+#   define NOMINMAX
+#endif
+#include <AK/Tools/Common/AkPlatformFuncs.h>

+ 20 - 0
Gems/AudioEngineWwise/Code/Platform/Common/Unimplemented/AudioEngineWwise_Unimplemented.cpp

@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/base.h>
+
+namespace Audio
+{
+    namespace Platform
+    {
+        void* InitializeSecondaryMemoryPool(size_t&)
+        {
+            return nullptr;
+        }
+    }
+}

+ 25 - 0
Gems/AudioEngineWwise/Code/Platform/Common/Unimplemented/AudioSystemImpl_wwise_Unimplemented.cpp

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioSystemImpl_wwise.h>
+
+#include <AK/SoundEngine/Common/AkSoundEngine.h>
+
+namespace Audio
+{
+    namespace Platform
+    {
+        void InitializeMemory()
+        {
+        }
+
+        void SetupAkSoundEngine(AkPlatformInitSettings&)
+        {
+        }
+    }
+}

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Linux/AkPlatformFuncs_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/AkPlatformFuncs_Default.h>

+ 28 - 0
Gems/AudioEngineWwise/Code/Platform/Linux/AudioEngineWwise_Traits_Linux.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#if defined(WWISE_RELEASE)
+#define AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND 1
+#else
+#define AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND 0
+#endif
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE 256 /* 256 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "256 (256 KiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_DEFAULT_SPEAKER_CONFIGURATION 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_FILEIO_AKDEVICE_THREAD_AFFINITY_MASK 0
+#if !defined(WWISE_RELEASE)
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE 64 /* 64 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "64 (64 KiB)"
+#endif
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE 128 << 10 /* 128 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "131072 (128 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE_DEFAULT_TEXT "0 (0 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE 2 << 10 /* 2 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE_DEFAULT_TEXT "2048 (2 MiB)"

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Linux/AudioEngineWwise_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AudioEngineWwise_Traits_Linux.h>

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Linux/FileIOHandler_wwise_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/FileIOHandler_wwise_Default.h>

+ 9 - 0
Gems/AudioEngineWwise/Code/Platform/Linux/PAL_linux.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE)

+ 12 - 0
Gems/AudioEngineWwise/Code/Platform/Linux/platform_linux.cmake

@@ -0,0 +1,12 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(LY_BUILD_DEPENDENCIES
+    PUBLIC
+        SDL2
+)

+ 19 - 0
Gems/AudioEngineWwise/Code/Platform/Linux/platform_linux_files.cmake

@@ -0,0 +1,19 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    ../Common/Default/AkPlatformFuncs_Default.h
+    ../Common/Default/FileIOHandler_wwise_Default.cpp
+    ../Common/Default/FileIOHandler_wwise_Default.h
+    ../Common/Unimplemented/AudioSystemImpl_wwise_Unimplemented.cpp
+    ../Common/Unimplemented/AudioEngineWwise_Unimplemented.cpp
+    AkPlatformFuncs_Platform.h
+    AudioEngineWwise_Traits_Linux.h
+    AudioEngineWwise_Traits_Platform.h
+    FileIOHandler_wwise_Platform.h
+)

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Mac/AkPlatformFuncs_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/AkPlatformFuncs_Default.h>

+ 28 - 0
Gems/AudioEngineWwise/Code/Platform/Mac/AudioEngineWwise_Traits_Mac.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#if defined(WWISE_RELEASE)
+#define AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND 1
+#else
+#define AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND 0
+#endif
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE 256 /* 256 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "256 (256 KiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_DEFAULT_SPEAKER_CONFIGURATION 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_FILEIO_AKDEVICE_THREAD_AFFINITY_MASK 0
+#if !defined(WWISE_RELEASE)
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE 64 /* 64 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "64 (64 KiB)"
+#endif
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE 128 << 10 /* 128 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "131072 (128 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE_DEFAULT_TEXT "0 (0 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE 2 << 10 /* 2 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE_DEFAULT_TEXT "2048 (2 MiB)"

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Mac/AudioEngineWwise_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AudioEngineWwise_Traits_Mac.h>

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Mac/FileIOHandler_wwise_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/FileIOHandler_wwise_Default.h>

+ 9 - 0
Gems/AudioEngineWwise/Code/Platform/Mac/PAL_mac.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE)

+ 18 - 0
Gems/AudioEngineWwise/Code/Platform/Mac/platform_mac.cmake

@@ -0,0 +1,18 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+find_library(AV_FOUNDATION_LIBRARY AVFoundation)
+find_library(AUDIO_TOOLBOX_GRAPHICS_LIBRARY AudioToolbox)
+find_library(CORE_AUDIO_SERVICES_LIBRARY CoreAudio)
+
+set(LY_BUILD_DEPENDENCIES
+    PRIVATE
+        ${AV_FOUNDATION_LIBRARY}
+        ${AUDIO_TOOLBOX_GRAPHICS_LIBRARY}
+        ${CORE_AUDIO_SERVICES_LIBRARY}
+)

+ 19 - 0
Gems/AudioEngineWwise/Code/Platform/Mac/platform_mac_files.cmake

@@ -0,0 +1,19 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    ../Common/Default/AkPlatformFuncs_Default.h
+    ../Common/Default/FileIOHandler_wwise_Default.cpp
+    ../Common/Default/FileIOHandler_wwise_Default.h
+    ../Common/Unimplemented/AudioSystemImpl_wwise_Unimplemented.cpp
+    ../Common/Unimplemented/AudioEngineWwise_Unimplemented.cpp
+    AkPlatformFuncs_Platform.h
+    AudioEngineWwise_Traits_Mac.h
+    AudioEngineWwise_Traits_Platform.h
+    FileIOHandler_wwise_Platform.h
+)

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Windows/AkPlatformFuncs_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/MSVC/AkPlatformFuncs_Default.h>

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Windows/AudioEngineWwise_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AudioEngineWwise_Traits_Windows.h>

+ 29 - 0
Gems/AudioEngineWwise/Code/Platform/Windows/AudioEngineWwise_Traits_Windows.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#if defined(WWISE_RELEASE)
+#define AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND 1
+#else
+#define AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND 0
+#endif
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE 256 /* 256 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "256 (256 KiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_DEFAULT_SPEAKER_CONFIGURATION 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_FILEIO_AKDEVICE_THREAD_AFFINITY_MASK 0
+#if !defined(WWISE_RELEASE)
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE 64 /* 64 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "64 (64 KiB)"
+#endif
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE 128 << 10 /* 128 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "131072 (128 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE_DEFAULT_TEXT "0 (0 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE 2 << 10 /* 2 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE_DEFAULT_TEXT "2048 (2 MiB)"
+

+ 28 - 0
Gems/AudioEngineWwise/Code/Platform/Windows/AudioSystemImpl_wwise_Windows.cpp

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioSystemImpl_wwise.h>
+
+#include <AK/SoundEngine/Common/AkSoundEngine.h>
+
+namespace Audio
+{
+    namespace Platform
+    {
+        void InitializeMemory()
+        {
+        }
+
+        void SetupAkSoundEngine(AkPlatformInitSettings& platformInitSettings)
+        {
+            platformInitSettings.threadBankManager.dwAffinityMask = 0;
+            platformInitSettings.threadLEngine.dwAffinityMask = 0;
+            platformInitSettings.threadMonitor.dwAffinityMask = 0;
+        }
+    }
+}

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/Windows/FileIOHandler_wwise_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/FileIOHandler_wwise_Default.h>

+ 9 - 0
Gems/AudioEngineWwise/Code/Platform/Windows/PAL_windows.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE)

+ 8 - 0
Gems/AudioEngineWwise/Code/Platform/Windows/platform_windows.cmake

@@ -0,0 +1,8 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+

+ 19 - 0
Gems/AudioEngineWwise/Code/Platform/Windows/platform_windows_files.cmake

@@ -0,0 +1,19 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    ../Common/MSVC/AkPlatformFuncs_Default.h
+    ../Common/Default/FileIOHandler_wwise_Default.cpp
+    ../Common/Default/FileIOHandler_wwise_Default.h
+    ../Common/Unimplemented/AudioEngineWwise_Unimplemented.cpp
+    AkPlatformFuncs_Platform.h
+    AudioSystemImpl_wwise_Windows.cpp
+    AudioEngineWwise_Traits_Platform.h
+    AudioEngineWwise_Traits_Windows.h
+    FileIOHandler_wwise_Platform.h
+)

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/iOS/AkPlatformFuncs_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/AkPlatformFuncs_Default.h>

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/iOS/AudioEngineWwise_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AudioEngineWwise_Traits_iOS.h>

+ 24 - 0
Gems/AudioEngineWwise/Code/Platform/iOS/AudioEngineWwise_Traits_iOS.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#define AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND 1
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE 256 /* 256 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "256 (256 KiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_DEFAULT_SPEAKER_CONFIGURATION 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_FILEIO_AKDEVICE_THREAD_AFFINITY_MASK 0
+#if !defined(WWISE_RELEASE)
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE 64 /* 64 KiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "64 (64 KiB)"
+#endif
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE 32 << 10 /* 32 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "32768 (32 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE 0
+#define AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE_DEFAULT_TEXT "0 (0 MiB)"
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE 2 << 10 /* 2 MiB */
+#define AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE_DEFAULT_TEXT "2048 (2 MiB)"

+ 10 - 0
Gems/AudioEngineWwise/Code/Platform/iOS/FileIOHandler_wwise_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <../Common/Default/FileIOHandler_wwise_Default.h>

+ 9 - 0
Gems/AudioEngineWwise/Code/Platform/iOS/PAL_ios.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_AUDIO_ENGINE_WWISE_SUPPORTED TRUE)

+ 20 - 0
Gems/AudioEngineWwise/Code/Platform/iOS/platform_ios.cmake

@@ -0,0 +1,20 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+find_library(AV_FOUNDATION_LIBRARY AVFoundation)
+find_library(AUDIO_TOOLBOX_GRAPHICS_LIBRARY AudioToolbox)
+find_library(CORE_AUDIO_SERVICES_LIBRARY CoreAudio)
+find_library(FOUNDATION_LIBRARY Foundation)
+
+set(LY_BUILD_DEPENDENCIES
+    PRIVATE
+        ${AV_FOUNDATION_LIBRARY}
+        ${AUDIO_TOOLBOX_GRAPHICS_LIBRARY}
+        ${CORE_AUDIO_SERVICES_LIBRARY}
+        ${FOUNDATION_LIBRARY}
+)

+ 19 - 0
Gems/AudioEngineWwise/Code/Platform/iOS/platform_ios_files.cmake

@@ -0,0 +1,19 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    ../Common/Default/AkPlatformFuncs_Default.h
+    ../Common/Default/FileIOHandler_wwise_Default.cpp
+    ../Common/Default/FileIOHandler_wwise_Default.h
+    ../Common/Unimplemented/AudioSystemImpl_wwise_Unimplemented.cpp
+    ../Common/Unimplemented/AudioEngineWwise_Unimplemented.cpp
+    AkPlatformFuncs_Platform.h
+    AudioEngineWwise_Traits_iOS.h
+    AudioEngineWwise_Traits_Platform.h
+    FileIOHandler_wwise_Platform.h
+)

+ 170 - 0
Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseGemSystemComponent.cpp

@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AudioEngineWwiseGemSystemComponent.h"
+
+#include <AzCore/PlatformIncl.h> // This include is needed to include WinSock2.h before including Windows.h
+ // As AK/SoundEngine/Common/AkTypes.h eventually includes Windows.h
+
+#include <AzCore/Console/ILogger.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/EditContextConstants.inl>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <AzFramework/Platform/PlatformDefaults.h>
+
+#include <AudioAllocators.h>
+#include <AudioSystemImplCVars.h>
+#include <AudioSystemImpl_wwise.h>
+#include <Common_wwise.h>
+#include <Config_wwise.h>
+
+#if defined(AUDIO_ENGINE_WWISE_EDITOR)
+    #include <AudioSystemEditor_wwise.h>
+#endif // AUDIO_ENGINE_WWISE_EDITOR
+
+
+namespace Audio
+{
+    namespace Platform
+    {
+        void* InitializeSecondaryMemoryPool(size_t& secondarySize);
+    }
+} // namespace Audio
+
+
+namespace AudioEngineWwiseGem
+{
+    void AudioEngineWwiseGemSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serialize->Class<AudioEngineWwiseGemSystemComponent, AZ::Component>()
+                ->Version(0)
+                ;
+
+            if (AZ::EditContext* ec = serialize->GetEditContext())
+            {
+                ec->Class<AudioEngineWwiseGemSystemComponent>("Audio Engine Wwise Gem", "Wwise implementation of the Audio Engine interfaces")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                        ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
+                    ;
+            }
+        }
+
+        Audio::Wwise::ConfigurationSettings::Reflect(context);
+    }
+
+    void AudioEngineWwiseGemSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        provided.push_back(AZ_CRC("AudioEngineService"));
+    }
+
+    void AudioEngineWwiseGemSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        incompatible.push_back(AZ_CRC("AudioEngineService"));
+    }
+
+    void AudioEngineWwiseGemSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        required.push_back(AZ_CRC("AudioSystemService"));
+    }
+
+    void AudioEngineWwiseGemSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+        dependent.push_back(AZ_CRC("AudioSystemService"));
+    }
+
+    void AudioEngineWwiseGemSystemComponent::Init()
+    {
+    }
+
+    void AudioEngineWwiseGemSystemComponent::Activate()
+    {
+        Audio::Gem::EngineRequestBus::Handler::BusConnect();
+
+    #if defined(AUDIO_ENGINE_WWISE_EDITOR)
+        AudioControlsEditor::EditorImplPluginEventBus::Handler::BusConnect();
+    #endif // AUDIO_ENGINE_WWISE_EDITOR
+    }
+
+    void AudioEngineWwiseGemSystemComponent::Deactivate()
+    {
+        Audio::Gem::EngineRequestBus::Handler::BusDisconnect();
+
+    #if defined(AUDIO_ENGINE_WWISE_EDITOR)
+        AudioControlsEditor::EditorImplPluginEventBus::Handler::BusDisconnect();
+    #endif // AUDIO_ENGINE_WWISE_EDITOR
+    }
+
+    bool AudioEngineWwiseGemSystemComponent::Initialize()
+    {
+        bool success = false;
+
+        // Check memory-related Wwise Cvars...
+        [[maybe_unused]] const AZ::u64 memorySubpartitionSizes = Audio::Wwise::Cvars::s_StreamDeviceMemorySize
+#if !defined(WWISE_RELEASE)
+            + Audio::Wwise::Cvars::s_MonitorQueueMemorySize
+#endif // !WWISE_RELEASE
+            + Audio::Wwise::Cvars::s_CommandQueueMemorySize;
+
+        AZ_Assert(Audio::Wwise::Cvars::s_PrimaryMemorySize > memorySubpartitionSizes,
+            "Wwise memory sizes of sub-categories add up to more than the primary memory pool size!")
+
+        AZ::SettingsRegistryInterface::FixedValueString assetPlatform = AzFramework::OSPlatformToDefaultAssetPlatform(
+            AZ_TRAIT_OS_PLATFORM_CODENAME);
+        if (assetPlatform.empty())
+        {
+            if (const auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+            {
+                AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, assetPlatform,
+                    AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "assets");
+            }
+        }
+
+        m_engineWwise = AZStd::make_unique<Audio::CAudioSystemImpl_wwise>(assetPlatform.c_str());
+        if (m_engineWwise)
+        {
+            AZLOG_INFO("%s", "AudioEngineWwise created!");
+
+            Audio::SystemRequest::Initialize initRequest;
+            AZ::Interface<Audio::IAudioSystem>::Get()->PushRequestBlocking(AZStd::move(initRequest));
+
+            success = true;
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Could not create AudioEngineWwise!");
+        }
+
+        return success;
+    }
+
+    void AudioEngineWwiseGemSystemComponent::Release()
+    {
+        m_engineWwise.reset();
+    }
+
+#if defined(AUDIO_ENGINE_WWISE_EDITOR)
+    void AudioEngineWwiseGemSystemComponent::InitializeEditorImplPlugin()
+    {
+        m_editorImplPlugin.reset(new AudioControls::CAudioSystemEditor_wwise());
+    }
+
+    void AudioEngineWwiseGemSystemComponent::ReleaseEditorImplPlugin()
+    {
+        m_editorImplPlugin.reset();
+    }
+
+    AudioControls::IAudioSystemEditor* AudioEngineWwiseGemSystemComponent::GetEditorImplPlugin()
+    {
+        return m_editorImplPlugin.get();
+    }
+#endif // AUDIO_ENGINE_WWISE_EDITOR
+
+} // namespace AudioEngineWwiseGem

+ 74 - 0
Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseGemSystemComponent.h

@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+
+#include <IAudioSystem.h>
+#include <IAudioSystemImplementation.h>
+
+#if defined(AUDIO_ENGINE_WWISE_EDITOR)
+    #include <AzCore/std/smart_ptr/unique_ptr.h>
+    #include <IAudioSystemEditor.h>
+#endif // AUDIO_ENGINE_WWISE_EDITOR
+
+struct SSystemInitParams;
+
+
+namespace AudioEngineWwiseGem
+{
+    class AudioEngineWwiseGemSystemComponent
+        : public AZ::Component
+        , protected Audio::Gem::EngineRequestBus::Handler
+    #if defined(AUDIO_ENGINE_WWISE_EDITOR)
+        , protected AudioControlsEditor::EditorImplPluginEventBus::Handler
+    #endif // AUDIO_ENGINE_WWISE_EDITOR
+    {
+    public:
+        AZ_COMPONENT(AudioEngineWwiseGemSystemComponent, "{521FA289-DC8B-4BC1-BBA0-A7D35EAC656E}", AZ::Component);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+
+    protected:
+        ////////////////////////////////////////////////////////////////////////
+        // Audio::Gem::EngineRequestBus interface implementation
+        bool Initialize() override;
+        void Release() override;
+        ////////////////////////////////////////////////////////////////////////
+
+    #if defined(AUDIO_ENGINE_WWISE_EDITOR)
+        ////////////////////////////////////////////////////////////////////////
+        // AudioControlsEditor::EditorImplPluginEventBus interface implementation
+        void InitializeEditorImplPlugin() override;
+        void ReleaseEditorImplPlugin() override;
+        AudioControls::IAudioSystemEditor* GetEditorImplPlugin() override;
+        ////////////////////////////////////////////////////////////////////////
+    #endif // AUDIO_ENGINE_WWISE_EDITOR
+
+        ////////////////////////////////////////////////////////////////////////
+        // AZ::Component interface implementation
+        void Init() override;
+        void Activate() override;
+        void Deactivate() override;
+        ////////////////////////////////////////////////////////////////////////
+
+    private:
+        AZStd::unique_ptr<Audio::AudioSystemImplementation> m_engineWwise;
+
+    #if defined(AUDIO_ENGINE_WWISE_EDITOR)
+        AZStd::unique_ptr<AudioControls::IAudioSystemEditor> m_editorImplPlugin;
+    #endif // AUDIO_ENGINE_WWISE_EDITOR
+    };
+
+} // namespace AudioEngineWwiseGem

+ 62 - 0
Gems/AudioEngineWwise/Code/Source/AudioEngineWwiseModule.cpp

@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/PlatformDef.h>
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <IGem.h>
+
+#include "AudioEngineWwiseGemSystemComponent.h"
+
+#if defined(AUDIO_ENGINE_WWISE_BUILDER)
+    #include <AudioControlBuilderComponent.h>
+    #include <WwiseBuilderComponent.h>
+#endif // AUDIO_ENGINE_WWISE_BUILDER
+
+namespace AudioEngineWwiseGem
+{
+    class AudioEngineWwiseModule
+        : public CryHooksModule
+    {
+    public:
+        AZ_RTTI(AudioEngineWwiseModule, "{303B0192-D866-4378-9342-728AA6E66F74}", CryHooksModule);
+        AZ_CLASS_ALLOCATOR(AudioEngineWwiseModule, AZ::SystemAllocator);
+
+        AudioEngineWwiseModule()
+            : CryHooksModule()
+        {
+            m_descriptors.insert(m_descriptors.end(), {
+            #if defined(AUDIO_ENGINE_WWISE_BUILDER)
+                AudioControlBuilder::BuilderPluginComponent::CreateDescriptor(),
+                WwiseBuilder::BuilderPluginComponent::CreateDescriptor(),
+            #else
+                AudioEngineWwiseGemSystemComponent::CreateDescriptor(),
+            #endif // AUDIO_ENGINE_WWISE_BUILDER
+            });
+        }
+
+        /**
+         * Add required SystemComponents to the SystemEntity.
+         */
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return AZ::ComponentTypeList {
+            #if !defined(AUDIO_ENGINE_WWISE_BUILDER)
+                azrtti_typeid<AudioEngineWwiseGemSystemComponent>(),
+            #endif // !AUDIO_ENGINE_WWISE_BUILDER
+            };
+        }
+    };
+
+} // namespace AudioEngineWwiseGem
+
+#if defined(O3DE_GEM_NAME)
+AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME), AudioEngineWwiseGem::AudioEngineWwiseModule)
+#else
+AZ_DECLARE_MODULE_CLASS(Gem_AudioEngineWwise, AudioEngineWwiseGem::AudioEngineWwiseModule)
+#endif

+ 52 - 0
Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderComponent.cpp

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioControlBuilderComponent.h>
+
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/Serialization/EditContextConstants.inl>
+
+namespace AudioControlBuilder
+{
+    void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<AudioControlBuilder::BuilderPluginComponent, AZ::Component>()
+                ->Version(1)
+                ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
+                ;
+        }
+    }
+
+    void BuilderPluginComponent::Activate()
+    {
+        // Register Audio Control builder
+        AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
+        builderDescriptor.m_name = "Audio Control Builder";
+        // pattern finds all Audio Control xml files in the libs/gameaudio folder and any of its subfolders.
+        builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("(.*libs\\/gameaudio\\/).*\\.xml", AssetBuilderSDK::AssetBuilderPattern::PatternType::Regex));
+        builderDescriptor.m_busId = azrtti_typeid<AudioControlBuilderWorker>();
+        builderDescriptor.m_version = 2;
+        builderDescriptor.m_createJobFunction = AZStd::bind(&AudioControlBuilderWorker::CreateJobs, &m_audioControlBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
+        builderDescriptor.m_processJobFunction = AZStd::bind(&AudioControlBuilderWorker::ProcessJob, &m_audioControlBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
+
+        // (optimization) this builder does not emit source dependencies:
+        builderDescriptor.m_flags |= AssetBuilderSDK::AssetBuilderDesc::BF_EmitsNoDependencies;
+
+        m_audioControlBuilder.BusConnect(builderDescriptor.m_busId);
+
+        AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Events::RegisterBuilderInformation, builderDescriptor);
+    }
+
+    void BuilderPluginComponent::Deactivate()
+    {
+        m_audioControlBuilder.BusDisconnect();
+    }
+
+} // namespace AudioControlBuilder

+ 46 - 0
Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderComponent.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+#include <AudioControlBuilderWorker.h>
+
+namespace AudioControlBuilder
+{
+    class BuilderPluginComponent
+        : public AZ::Component
+    {
+    public:
+        AZ_COMPONENT(BuilderPluginComponent, "{4C0E0008-3D09-4628-8CEE-E9C6475AFB62}");
+
+        BuilderPluginComponent() = default;
+        ~BuilderPluginComponent() override = default;
+
+        static void Reflect(AZ::ReflectContext* context);
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+        {
+            provided.push_back(AZ_CRC_CE("AudioControlBuilderService"));
+        }
+
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+        {
+            incompatible.push_back(AZ_CRC_CE("AudioControlBuilderService"));
+        }
+
+        //////////////////////////////////////////////////////////////////////////
+        // AZ::Component interface
+        void Activate() override;
+        void Deactivate() override;
+        //////////////////////////////////////////////////////////////////////////
+
+    private:
+        AudioControlBuilderWorker m_audioControlBuilder;
+    };
+
+} // namespace AudioControlBuilder

+ 550 - 0
Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp

@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioControlBuilderWorker.h>
+
+#include <AzCore/AzCore_Traits_Platform.h>
+#include <AzCore/IO/SystemFile.h>
+#include <AzCore/JSON/rapidjson.h>
+#include <AzCore/JSON/document.h>
+#include <AzCore/PlatformId/PlatformId.h>
+#include <AzCore/StringFunc/StringFunc.h>
+#include <AzFramework/IO/LocalFileIO.h>
+
+#include <ATLCommon.h>
+#include <Common_wwise.h>
+#include <Config_wwise.h>
+
+namespace AudioControlBuilder
+{
+    namespace Internal
+    {
+        const char JsonEventsKey[] = "includedEvents";
+        const char SoundbankDependencyFileExtension[] = ".bankdeps";
+
+        const char NodeDoesNotExistMessage[] = "%s node does not exist. Please be sure that you have defined at least one %s for this Audio Control file.\n";
+        const char MalformedNodeMissingAttributeMessage[] = "%s node is malformed: does not have an attribute %s defined. This is likely the result of manual editing. Please resave the Audio Control file.\n";
+        const char MalformedNodeMissingChildNodeMessage[] = "%s node does not contain a child %s node. This is likely the result of manual editing. Please resave the Audio Control file.\n";
+
+        namespace Legacy
+        {
+            using AtlConfigGroupMap = AZStd::unordered_map<AZStd::string, const AZ::rapidxml::xml_node<char>*>;
+
+            AZStd::string GetAtlPlatformName(const AZStd::string& requestPlatform)
+            {
+                AZStd::string atlPlatform;
+                AZStd::string platform = requestPlatform;
+
+                // When debugging a builder using a debug task, it replaces platform tags with "debug platform". Make sure the builder
+                //  actually uses the host platform identifier when going through this function in this case.
+                if (platform == "debug platform")
+                {
+                    atlPlatform = AZ_TRAIT_OS_PLATFORM_NAME;
+                    AZStd::to_lower(atlPlatform.begin(), atlPlatform.end());
+                }
+                else
+                {
+                    if (platform == "pc")
+                    {
+                        atlPlatform = "windows";
+                    }
+                    else if (platform == "android")
+                    {
+                        atlPlatform = "android";
+                    }
+                    else if (platform == "mac")
+                    {
+                        atlPlatform = "mac";
+                    }
+                    else
+                    {
+                        atlPlatform = AZStd::move(platform);
+                    }
+                }
+                return AZStd::move(atlPlatform);
+            }
+
+            AZ::Outcome<void, AZStd::string> BuildConfigGroupMap(const AZ::rapidxml::xml_node<char>* preloadRequestNode, AtlConfigGroupMap& configGroupMap)
+            {
+                AZ_Assert(preloadRequestNode != nullptr, NodeDoesNotExistMessage, Audio::ATLXmlTags::ATLPreloadRequestTag, "preload request");
+
+                auto configGroupNode = preloadRequestNode->first_node(Audio::ATLXmlTags::ATLConfigGroupTag);
+                while (configGroupNode)
+                {
+                    // Populate the config group map by iterating over all ATLConfigGroup nodes, and place each one in the map keyed by the group's atl_name attribute...
+                    if (const auto configGroupNameAttr = configGroupNode->first_attribute(Audio::ATLXmlTags::ATLNameAttribute))
+                    {
+                        configGroupMap.emplace(configGroupNameAttr->value(), configGroupNode);
+                    }
+                    else
+                    {
+                        return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
+                            Audio::ATLXmlTags::ATLConfigGroupTag, Audio::ATLXmlTags::ATLNameAttribute));
+                    }
+
+                    configGroupNode = configGroupNode->next_sibling(Audio::ATLXmlTags::ATLConfigGroupTag);
+                }
+
+                // If no config groups are defined, this is an empty preload request with no banks referenced, which is valid.
+                return AZ::Success();
+            }
+
+            AZ::Outcome<void, AZStd::string> GetBanksFromAtlPreloads(const AZ::rapidxml::xml_node<char>* preloadsNode, const AZStd::string& atlPlatformIdentifier, AZStd::vector<AZStd::string>& banksReferenced)
+            {
+                AZ_Assert(preloadsNode != nullptr, NodeDoesNotExistMessage, Audio::ATLXmlTags::PreloadsNodeTag, "preload request");
+
+                auto preloadRequestNode = preloadsNode->first_node(Audio::ATLXmlTags::ATLPreloadRequestTag);
+                if (!preloadRequestNode)
+                {
+                    return AZ::Failure(AZStd::string::format(NodeDoesNotExistMessage,
+                        Audio::ATLXmlTags::ATLPreloadRequestTag, "preload request"));
+                }
+
+                // For each preload request in the control file, determine which config group is used for this platform and register each
+                // bank listed in that preload request as a dependency.
+                while (preloadRequestNode)
+                {
+                    AtlConfigGroupMap configGroupMap;
+                    auto configGroupMapResult = BuildConfigGroupMap(preloadRequestNode, configGroupMap);
+
+                    // If the returned map is empty, there are not banks referenced in the preload request, return the result here.
+                    if (!configGroupMapResult.IsSuccess() || configGroupMap.size() == 0)
+                    {
+                        return configGroupMapResult;
+                    }
+
+                    const auto platformsNode = preloadRequestNode->first_node(Audio::ATLXmlTags::ATLPlatformsTag);
+                    if (!platformsNode)
+                    {
+                        return AZ::Failure(AZStd::string::format(MalformedNodeMissingChildNodeMessage,
+                            Audio::ATLXmlTags::ATLPreloadRequestTag, Audio::ATLXmlTags::ATLPlatformsTag));
+                    }
+
+                    auto platformNode = platformsNode->first_node(Audio::ATLXmlTags::PlatformNodeTag);
+                    if (!platformNode)
+                    {
+                        return AZ::Failure(AZStd::string::format(MalformedNodeMissingChildNodeMessage,
+                            Audio::ATLXmlTags::ATLPlatformsTag, Audio::ATLXmlTags::PlatformNodeTag));
+                    }
+
+                    AZStd::string configGroupName;
+                    // For each platform node in the platform list, check the atl_name to see if it matches the platform the request is
+                    //  intended for. If it is, grab the name of the config group that is used for that platform to load it.
+                    while (platformNode)
+                    {
+                        const auto atlNameAttr = platformNode->first_attribute(Audio::ATLXmlTags::ATLNameAttribute);
+                        if (!atlNameAttr)
+                        {
+                            return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
+                                Audio::ATLXmlTags::PlatformNodeTag, Audio::ATLXmlTags::ATLNameAttribute));
+                        }
+                        else if (atlPlatformIdentifier == atlNameAttr->value())
+                        {
+                            // We've found the right platform that matches the request, so grab the group name and stop looking through
+                            //  the list
+                            const auto configGroupNameAttr = platformNode->first_attribute(Audio::ATLXmlTags::ATLConfigGroupAttribute);
+                            if (!configGroupNameAttr)
+                            {
+                                return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
+                                    Audio::ATLXmlTags::PlatformNodeTag, Audio::ATLXmlTags::ATLConfigGroupAttribute));
+                            }
+                            configGroupName = configGroupNameAttr->value();
+                            break;
+                        }
+
+                        platformNode = platformNode->next_sibling(Audio::ATLXmlTags::PlatformNodeTag);
+                    }
+
+                    const AZ::rapidxml::xml_node<char>* configGroupNode = configGroupMap[configGroupName];
+                    if (!configGroupNode)
+                    {
+                        // The config group this platform uses isn't defined in the control file. This might be intentional, so just
+                        //  generate a warning and keep going to the next preload node.
+                        AZ_TracePrintf("Audio Control Builder", "%s node for config group %s is not defined, so no banks are referenced.",
+                            Audio::ATLXmlTags::ATLConfigGroupTag, configGroupName.c_str());
+                    }
+                    else
+                    {
+                        auto wwiseFileNode = configGroupNode->first_node(Audio::WwiseXmlTags::WwiseFileTag);
+                        if (!wwiseFileNode)
+                        {
+                            return AZ::Failure(AZStd::string::format(MalformedNodeMissingChildNodeMessage,
+                                Audio::ATLXmlTags::ATLConfigGroupTag, Audio::WwiseXmlTags::WwiseFileTag));
+                        }
+
+                        // For each WwiseFile (soundbank) referenced in the config group, grab the file name and add it to the reference list
+                        while (wwiseFileNode)
+                        {
+                            const auto bankNameAttribute = wwiseFileNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute);
+                            if (!bankNameAttribute)
+                            {
+                                return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
+                                    Audio::WwiseXmlTags::WwiseFileTag, Audio::WwiseXmlTags::WwiseNameAttribute));
+                            }
+
+                            // Prepend the bank name with the relative path to the wwise sounds folder to get relative path to the bank from
+                            //  the @products@ alias and push that into the list of banks referenced.
+                            AZStd::string soundsPrefix = Audio::Wwise::DefaultBanksPath;
+                            banksReferenced.emplace_back(soundsPrefix + bankNameAttribute->value());
+
+                            wwiseFileNode = wwiseFileNode->next_sibling(Audio::WwiseXmlTags::WwiseFileTag);
+                        }
+                    }
+
+                    preloadRequestNode = preloadRequestNode->next_sibling(Audio::ATLXmlTags::ATLPreloadRequestTag);
+                }
+
+                return AZ::Success();
+            }
+
+        } // namespace Legacy
+
+
+        AZ::Outcome<void, AZStd::string> BuildAtlEventList(const AZ::rapidxml::xml_node<char>* triggersNode, AZStd::vector<AZStd::string>& eventNames)
+        {
+            AZ_Assert(triggersNode != nullptr, NodeDoesNotExistMessage, Audio::ATLXmlTags::TriggersNodeTag, "trigger");
+
+            auto triggerNode = triggersNode->first_node(Audio::ATLXmlTags::ATLTriggerTag);
+            while (triggerNode)
+            {
+                // For each audio trigger, push the name of the Wwise event (if assigned) into the list.
+                // It's okay for an ATLTrigger node to not have a Wwise event associated with it.
+                if (const auto eventNode = triggerNode->first_node(Audio::WwiseXmlTags::WwiseEventTag))
+                {
+                    if (const auto eventNameAttr = eventNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute))
+                    {
+                        eventNames.push_back(eventNameAttr->value());
+                    }
+                    else
+                    {
+                        return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
+                            Audio::WwiseXmlTags::WwiseEventTag, Audio::WwiseXmlTags::WwiseNameAttribute));
+                    }
+                }
+
+                triggerNode = triggerNode->next_sibling(Audio::ATLXmlTags::ATLTriggerTag);
+            }
+
+            return AZ::Success();
+        }
+
+        AZ::Outcome<void, AZStd::string> GetBanksFromAtlPreloads(const AZ::rapidxml::xml_node<char>* preloadsNode, AZStd::vector<AZStd::string>& banksReferenced)
+        {
+            AZ_Assert(preloadsNode != nullptr, NodeDoesNotExistMessage, Audio::ATLXmlTags::PreloadsNodeTag, "preload request");
+
+            auto preloadRequestNode = preloadsNode->first_node(Audio::ATLXmlTags::ATLPreloadRequestTag);
+            if (!preloadRequestNode)
+            {
+                return AZ::Failure(AZStd::string::format(NodeDoesNotExistMessage, Audio::ATLXmlTags::ATLPreloadRequestTag, "preload request"));
+            }
+
+            // Loop through the ATLPreloadRequest nodes...
+            // Find any Wwise banks listed and add them to the banksReferenced vector.
+            while (preloadRequestNode)
+            {
+                // Attempt to find the child node in the New Xml format...
+                if (auto wwiseFileNode = preloadRequestNode->first_node(Audio::WwiseXmlTags::WwiseFileTag))
+                {
+                    while (wwiseFileNode)
+                    {
+                        const auto bankNameAttr = wwiseFileNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute);
+                        if (bankNameAttr)
+                        {
+                            AZStd::string soundsPrefix = Audio::Wwise::DefaultBanksPath;
+                            banksReferenced.emplace_back(soundsPrefix + bankNameAttr->value());
+                        }
+                        else
+                        {
+                            return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
+                                Audio::WwiseXmlTags::WwiseFileTag, Audio::WwiseXmlTags::WwiseNameAttribute));
+                        }
+
+                        wwiseFileNode = wwiseFileNode->next_sibling(Audio::WwiseXmlTags::WwiseFileTag);
+                    }
+                }
+                else
+                {
+                    return AZ::Failure(AZStd::string::format("Preloads Xml appears to be in an older format, trying Legacy parsing.\n"));
+                }
+
+                preloadRequestNode = preloadRequestNode->next_sibling(Audio::ATLXmlTags::ATLPreloadRequestTag);
+            }
+
+            return AZ::Success();
+        }
+
+        AZ::Outcome<void, AZStd::string> GetEventsFromBankMetadata(const rapidjson::Value& rootObject, AZStd::set<AZStd::string>& eventNames)
+        {
+            if (!rootObject.IsObject())
+            {
+                return AZ::Failure(AZStd::string("The root of the metadata file is not an object. Please regenerate the metadata for this soundbank."));
+            }
+
+            // If the file doesn't define an events field, then there are no events in the bank
+            if (!rootObject.HasMember(JsonEventsKey))
+            {
+                return AZ::Success();
+            }
+
+            const rapidjson::Value& eventsArray = rootObject[JsonEventsKey];
+            if (!eventsArray.IsArray())
+            {
+                return AZ::Failure(AZStd::string("Events field is not an array. Please regenerate the metadata for this soundbank."));
+            }
+
+            for (rapidjson::SizeType eventIndex = 0; eventIndex < eventsArray.Size(); ++eventIndex)
+            {
+                eventNames.emplace(eventsArray[eventIndex].GetString());
+            }
+
+            return AZ::Success();
+        }
+
+        AZ::Outcome<void, AZStd::string> GetEventsFromBank(const AZStd::string& bankMetadataPath, AZStd::set<AZStd::string>& eventNames)
+        {
+            if (!AZ::IO::SystemFile::Exists(bankMetadataPath.c_str()))
+            {
+                return AZ::Failure(AZStd::string::format("Failed to find the soundbank metadata file %s. Full dependency information cannot be determined without the metadata file. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str()));
+            }
+
+            AZ::u64 fileSize = AZ::IO::SystemFile::Length(bankMetadataPath.c_str());
+            if (fileSize == 0)
+            {
+                return AZ::Failure(AZStd::string::format("Soundbank metadata file at path %s is an empty file. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str()));
+            }
+
+            AZStd::vector<char> buffer(fileSize + 1);
+            buffer[fileSize] = 0;
+            if (!AZ::IO::SystemFile::Read(bankMetadataPath.c_str(), buffer.data()))
+            {
+                return AZ::Failure(AZStd::string::format("Failed to read the soundbank metadata file at path %s. Please make sure the file is not open or being edited by another program.", bankMetadataPath.c_str()));
+            }
+
+            rapidjson::Document bankMetadataDoc;
+            bankMetadataDoc.Parse(buffer.data());
+            if (bankMetadataDoc.GetParseError() != rapidjson::ParseErrorCode::kParseErrorNone)
+            {
+                return AZ::Failure(AZStd::string::format("Failed to parse soundbank metadata at path %s into JSON. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str()));
+            }
+
+            return GetEventsFromBankMetadata(bankMetadataDoc, eventNames);
+        }
+
+    } // namespace Internal
+
+
+
+    AudioControlBuilderWorker::AudioControlBuilderWorker()
+        : m_globalScopeControlsPath("libs/gameaudio/")
+        , m_isShuttingDown(false)
+    {
+        AZ::StringFunc::Path::Normalize(m_globalScopeControlsPath);
+    }
+
+    void AudioControlBuilderWorker::ShutDown()
+    {
+        m_isShuttingDown = true;
+    }
+
+    void AudioControlBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
+    {
+        if (m_isShuttingDown)
+        {
+            response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
+            return;
+        }
+
+        for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
+        {
+            if (info.m_identifier == "server")
+            {
+                continue;
+            }
+
+            AssetBuilderSDK::JobDescriptor descriptor;
+            descriptor.m_jobKey = "Audio Control";
+            descriptor.m_critical = true;
+            descriptor.SetPlatformIdentifier(info.m_identifier.c_str());
+            descriptor.m_priority = 0;
+            response.m_createJobOutputs.push_back(descriptor);
+        }
+
+        response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
+    }
+
+    void AudioControlBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
+    {
+        AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "AudioControlBuilderWorker Starting Job.\n");
+
+        if (m_isShuttingDown)
+        {
+            AZ_TracePrintf(AssetBuilderSDK::WarningWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str());
+            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
+            return;
+        }
+
+        AZStd::string fileName;
+        AZ::StringFunc::Path::GetFullFileName(request.m_fullPath.c_str(), fileName);
+
+        AssetBuilderSDK::JobProduct jobProduct(request.m_fullPath);
+
+        if (!ParseProductDependencies(request, jobProduct.m_dependencies, jobProduct.m_pathDependencies))
+        {
+            AZ_Error(AssetBuilderSDK::ErrorWindow, false, "Error during parsing product dependencies for asset %s.\n", fileName.c_str());
+            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
+            return;
+        }
+
+        response.m_outputProducts.push_back(jobProduct);
+        response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
+    }
+
+    bool AudioControlBuilderWorker::ParseProductDependencies(
+        const AssetBuilderSDK::ProcessJobRequest& request,
+        AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
+        AssetBuilderSDK::ProductPathDependencySet& pathDependencies)
+    {
+        AZ::IO::FileIOStream fileStream;
+        if (!fileStream.Open(request.m_fullPath.c_str(), AZ::IO::OpenMode::ModeRead))
+        {
+            return false;
+        }
+
+        AZ::IO::SizeType length = fileStream.GetLength();
+        if (length == 0)
+        {
+            return false;
+        }
+
+        AZStd::vector<char> charBuffer;
+        charBuffer.resize_no_construct(length + 1);
+        fileStream.Read(length, charBuffer.data());
+        charBuffer.back() = 0;
+
+        // Get the XML root node
+        AZ::rapidxml::xml_document<char> xmlDoc;
+        if (!xmlDoc.parse<AZ::rapidxml::parse_no_data_nodes>(charBuffer.data()))
+        {
+            return false;
+        }
+
+        AZ::rapidxml::xml_node<char>* xmlRootNode = xmlDoc.first_node();
+        if (!xmlRootNode)
+        {
+            return false;
+        }
+
+        ParseProductDependenciesFromXmlFile(xmlRootNode,
+            request.m_fullPath,
+            request.m_sourceFile,
+            request.m_platformInfo.m_identifier,
+            productDependencies,
+            pathDependencies);
+
+        return true;
+    }
+
+    void AudioControlBuilderWorker::ParseProductDependenciesFromXmlFile(
+        const AZ::rapidxml::xml_node<char>* node,
+        const AZStd::string& fullPath,
+        [[maybe_unused]] const AZStd::string& sourceFile,
+        const AZStd::string& platformIdentifier,
+        [[maybe_unused]] AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
+        AssetBuilderSDK::ProductPathDependencySet& pathDependencies)
+    {
+        AZ_Assert(node != nullptr, "AudioControlBuilderWorker::ParseProductDependenciesFromXmlFile - null xml root node!\n");
+
+        const auto preloadsNode = node->first_node(Audio::ATLXmlTags::PreloadsNodeTag);
+        if (!preloadsNode)
+        {
+            // No preloads were defined in this control file, so we can return. If triggers are defined in this preload file, we can't
+            // validate that they'll be playable because we are unsure of what other control files for the given scope are defined.
+            return;
+        }
+
+        // Collect any references to soundbanks, initially use the newer parsing format...
+        AZStd::vector<AZStd::string> banksReferenced;
+        AZ::Outcome<void, AZStd::string> gatherBankReferencesResult = Internal::GetBanksFromAtlPreloads(preloadsNode, banksReferenced);
+        if (!gatherBankReferencesResult)
+        {
+            // Legacy...
+            // Convert platform name to platform name that is used by wwise and ATL.
+            AZStd::string atlPlatformName = AZStd::move(Internal::Legacy::GetAtlPlatformName(platformIdentifier));
+            gatherBankReferencesResult = Internal::Legacy::GetBanksFromAtlPreloads(preloadsNode, atlPlatformName, banksReferenced);
+        }
+
+        if (!gatherBankReferencesResult)
+        {
+            AZ_Warning("Audio Control Builder", false, "Failed to gather product dependencies for Audio Control file %s.  %s\n",
+                sourceFile.c_str(), gatherBankReferencesResult.GetError().c_str());
+            return;
+        }
+
+        if (banksReferenced.size() == 0)
+        {
+            // If there are no banks referenced, then there are no dependencies to register, so return.
+            return;
+        }
+
+        for (const AZStd::string& relativeBankPath : banksReferenced)
+        {
+            pathDependencies.emplace(relativeBankPath, AssetBuilderSDK::ProductPathDependencyType::ProductFile);
+        }
+
+        // For each bank figure out what events are included in the bank, then run through every event referenced in the file and
+        //  make sure it is in the list gathered from the banks.
+        const auto triggersNode = node->first_node(Audio::ATLXmlTags::TriggersNodeTag);
+        if (!triggersNode)
+        {
+            // No triggers were defined in this file, so we don't need to do any event validation.
+            return;
+        }
+
+        AZStd::vector<AZStd::string> eventsReferenced;
+        AZ::Outcome<void, AZStd::string> gatherEventReferencesResult = Internal::BuildAtlEventList(triggersNode, eventsReferenced);
+        if (!gatherEventReferencesResult.IsSuccess())
+        {
+            AZ_Warning("Audio Control Builder", false, "Failed to gather list of events referenced by Audio Control file %s. %s",
+                sourceFile.c_str(), gatherEventReferencesResult.GetError().c_str());
+            return;
+        }
+
+        AZStd::string projectSourcePath = fullPath;
+        AZ::u64 firstSubDirectoryIndex = AZ::StringFunc::Find(projectSourcePath, m_globalScopeControlsPath);
+        AZ::StringFunc::LKeep(projectSourcePath, firstSubDirectoryIndex);
+
+        AZStd::set<AZStd::string> wwiseEventsInReferencedBanks;
+
+        // Load all bankdeps files for all banks referenced and aggregate the list of events in those files.
+        for (const AZStd::string& relativeBankPath : banksReferenced)
+        {
+            // Create the full path to the bankdeps file from the bank file.
+            AZStd::string bankMetadataPath;
+            AZ::StringFunc::Path::Join(projectSourcePath.c_str(), relativeBankPath.c_str(), bankMetadataPath);
+            AZ::StringFunc::Path::ReplaceExtension(bankMetadataPath, Internal::SoundbankDependencyFileExtension);
+
+            AZ::Outcome<void, AZStd::string> getReferencedEventsResult = Internal::GetEventsFromBank(bankMetadataPath, wwiseEventsInReferencedBanks);
+            if (!getReferencedEventsResult.IsSuccess())
+            {
+                // only warn if we couldn't get info from a bankdeps file. Won't impact registering dependencies, but used to help
+                // customers potentially debug issues.
+                AZ_Warning("Audio Control Builder", false, "Failed to gather list of events referenced by soundbank %s. %s", relativeBankPath.c_str(), getReferencedEventsResult.GetError().c_str());
+            }
+        }
+
+        // Confirm that each event referenced by the file exists in the list of events available from the banks referenced.
+        for (const AZStd::string& eventInControlFile : eventsReferenced)
+        {
+            if (wwiseEventsInReferencedBanks.find(eventInControlFile) == wwiseEventsInReferencedBanks.end())
+            {
+                AZ_Warning("Audio Control Builder", false, "Failed to find Wwise event %s in the list of events contained in banks referenced by %s. Event may fail to play properly.", eventInControlFile.c_str(), sourceFile.c_str());
+            }
+        }
+    }
+
+} // namespace AudioControlBuilder

+ 54 - 0
Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.h

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/parallel/atomic.h>
+#include <AzCore/XML/rapidxml.h>
+#include <AssetBuilderSDK/AssetBuilderBusses.h>
+#include <AssetBuilderSDK/AssetBuilderSDK.h>
+
+namespace AudioControlBuilder
+{
+    //! The Audio Control Builder Worker handles scanning XML files that are output by the Audio Controls editor
+    //! for asset references to audio files and registers those files as product dependencies.
+    class AudioControlBuilderWorker
+        : public AssetBuilderSDK::AssetBuilderCommandBus::Handler
+    {
+    public:
+        AZ_RTTI(AudioControlBuilderWorker, "{3AD18978-9025-482A-B06A-17EF0EB4D7CA}");
+
+        AudioControlBuilderWorker();
+        ~AudioControlBuilderWorker() = default;
+
+        //! Asset Builder Callback Functions
+        void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response);
+        void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
+
+        //! AssetBuilderSDK::AssetBuilderCommandBus interface
+        void ShutDown() override;
+
+        bool ParseProductDependencies(
+            const AssetBuilderSDK::ProcessJobRequest& request,
+            AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
+            AssetBuilderSDK::ProductPathDependencySet& pathDependencies);
+
+    private:
+        void ParseProductDependenciesFromXmlFile(
+            const AZ::rapidxml::xml_node<char>* node,
+            const AZStd::string& fullPath,
+            const AZStd::string& sourceFile,
+            const AZStd::string& platformIdentifier,
+            AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
+            AssetBuilderSDK::ProductPathDependencySet& pathDependencies);
+
+        AZStd::string m_globalScopeControlsPath;
+        AZStd::atomic_bool m_isShuttingDown;
+    };
+
+} // namespace AudioControlBuilder

+ 52 - 0
Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderComponent.cpp

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <WwiseBuilderComponent.h>
+
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/Serialization/EditContextConstants.inl>
+
+namespace WwiseBuilder
+{
+    void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<WwiseBuilder::BuilderPluginComponent, AZ::Component>()
+                ->Version(1)
+                ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
+                ;
+        }
+    }
+
+    void BuilderPluginComponent::Activate()
+    {
+        // Register Wwise builder
+        AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
+        builderDescriptor.m_name = "Wwise Builder";
+        builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.bnk", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
+        builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.wem", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
+        builderDescriptor.m_busId = azrtti_typeid<WwiseBuilderWorker>();
+        builderDescriptor.m_version = 2;
+        builderDescriptor.m_createJobFunction = AZStd::bind(&WwiseBuilderWorker::CreateJobs, &m_wwiseBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
+        builderDescriptor.m_processJobFunction = AZStd::bind(&WwiseBuilderWorker::ProcessJob, &m_wwiseBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
+
+        // (optimization) this builder does not emit source dependencies:
+        builderDescriptor.m_flags |= AssetBuilderSDK::AssetBuilderDesc::BF_EmitsNoDependencies;
+
+        m_wwiseBuilder.BusConnect(builderDescriptor.m_busId);
+
+        AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Events::RegisterBuilderInformation, builderDescriptor);
+    }
+
+    void BuilderPluginComponent::Deactivate()
+    {
+        m_wwiseBuilder.BusDisconnect();
+    }
+
+} // namespace WwiseBuilder

+ 46 - 0
Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderComponent.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+#include <WwiseBuilderWorker.h>
+
+namespace WwiseBuilder
+{
+    class BuilderPluginComponent
+        : public AZ::Component
+    {
+    public:
+        AZ_COMPONENT(BuilderPluginComponent, "{8630414A-0BA6-4759-809A-C6903994AE30}");
+
+        BuilderPluginComponent() = default;
+        ~BuilderPluginComponent() override = default;
+
+        static void Reflect(AZ::ReflectContext* context);
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+        {
+            provided.push_back(AZ_CRC_CE("WwiseBuilderService"));
+        }
+
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+        {
+            incompatible.push_back(AZ_CRC_CE("WwiseBuilderService"));
+        }
+
+        //////////////////////////////////////////////////////////////////////////
+        // AZ::Component interface
+        void Activate() override;
+        void Deactivate() override;
+        //////////////////////////////////////////////////////////////////////////
+
+    private:
+        WwiseBuilderWorker m_wwiseBuilder;
+    };
+
+} // namespace WwiseBuilder

+ 314 - 0
Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderWorker.cpp

@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <WwiseBuilderWorker.h>
+
+#include <AzCore/Debug/Trace.h>
+#include <AzCore/IO/FileIO.h>
+#include <AzCore/IO/Path/Path.h>
+#include <AzCore/IO/SystemFile.h>
+#include <AzCore/JSON/document.h>
+#include <AzCore/JSON/rapidjson.h>
+#include <AzCore/StringFunc/StringFunc.h>
+
+namespace WwiseBuilder
+{
+    const char WwiseBuilderWindowName[] = "WwiseBuilder";
+
+    namespace Internal
+    {
+        const char SoundbankDependencyFileExtension[] = ".bankdeps";
+        const char JsonDependencyKey[] = "dependencies";
+
+        AZ::Outcome<AZStd::string, AZStd::string> GetDependenciesFromMetadata(const rapidjson::Value& rootObject, AZStd::vector<AZStd::string>& fileNames)
+        {
+            if (!rootObject.IsObject())
+            {
+                return AZ::Failure(AZStd::string("The root of the metadata file is not an object.  "
+                    "Please regenerate the dependencies metadata for this soundbank."));
+            }
+
+            // If the file doesn't define a dependency field, then there are no dependencies.
+            if (!rootObject.HasMember(JsonDependencyKey))
+            {
+                AZStd::string addingDefaultDependencyWarning = AZStd::string::format(
+                    "Dependencies array does not exist - the .bankdeps file may have been manually edited.  "
+                    "Registering a default dependency on %s.  Dependencies may need to be regenerated via the authoring tool scripts.",
+                    Audio::Wwise::InitBank);
+                fileNames.push_back(Audio::Wwise::InitBank);
+                return AZ::Success(addingDefaultDependencyWarning);
+            }
+
+            const rapidjson::Value& dependenciesArray = rootObject[JsonDependencyKey];
+            if (!dependenciesArray.IsArray())
+            {
+                return AZ::Failure(AZStd::string("Dependency field is not an array. Please regenerate the dependencies metadata for this soundbank."));
+            }
+
+            for (rapidjson::SizeType dependencyIndex = 0; dependencyIndex < dependenciesArray.Size(); ++dependencyIndex)
+            {
+                fileNames.push_back(dependenciesArray[dependencyIndex].GetString());
+            }
+
+            // Make sure init.bnk is a dependency. Force-add it if it's not.
+            // Look for init.bnk in the dependencies file list...
+            auto iter = AZStd::find_if(
+                fileNames.begin(), fileNames.end(),
+                [](AZStd::string fileName) -> bool
+                {
+                    // use a string copy argument in order to to_lower it...
+                    AZStd::to_lower(fileName.begin(), fileName.end());
+                    return fileName == Audio::Wwise::InitBank;
+                });
+            if (iter == fileNames.end())
+            {
+                // Init bank wasn't found, which likely means it was modified by hand. However, every bank is dependent
+                // on init.bnk (other than itself), so force-add it as a dependency here and return a warning message.
+                AZStd::string dependencyWarning;
+                if (fileNames.empty())
+                {
+                    dependencyWarning = AZStd::string::format(
+                        "Dependencies array is empty - the .bankdeps file may have been manually edited.  "
+                        "Registering a default dependency on %s.  Dependencies may need to be regenerated via the authoring tool scripts.",
+                        Audio::Wwise::InitBank);
+                }
+                else
+                {
+                    dependencyWarning = AZStd::string::format(
+                        "Dependencies did not contain the initialization bank - it may have been manually removed from the .bankdeps file.  "
+                        "It is necessary for all banks to declare %s as a dependency, so it has been automatically added.  "
+                        "Dependencies may need to be regenerated via the authoring tool scripts.",
+                        Audio::Wwise::InitBank);
+                }
+                fileNames.push_back(Audio::Wwise::InitBank);
+                return AZ::Success(dependencyWarning);
+            }
+
+            return AZ::Success(AZStd::string());
+        }
+    }
+
+
+    WwiseBuilderWorker::WwiseBuilderWorker()
+        : m_isShuttingDown(false)
+    {
+    }
+
+    void WwiseBuilderWorker::ShutDown()
+    {
+        // This will be called on a different thread than the process job thread
+        m_isShuttingDown = true;
+    }
+
+    void WwiseBuilderWorker::Initialize()
+    {
+        AZ::IO::Path configFile("@projectroot@");
+        configFile /= Audio::Wwise::DefaultBanksPath;
+        configFile /= Audio::Wwise::ConfigFile;
+
+        if (AZ::IO::FileIOBase::GetInstance()->Exists(configFile.c_str()))
+        {
+            m_wwiseConfig.Load(configFile.Native());
+        }
+
+        m_initialized = true;
+    }
+
+    // This happens early on in the file scanning pass.
+    // This function should always create the same jobs and not do any checking whether the job is up to date.
+    void WwiseBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
+    {
+        if (m_isShuttingDown)
+        {
+            response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
+            return;
+        }
+
+        if (!m_initialized)
+        {
+            Initialize();
+        }
+
+        AZStd::string jobKey = "Wwise";
+        if (AZ::StringFunc::EndsWith(request.m_sourceFile, Audio::Wwise::MediaExtension))
+        {
+            jobKey.append(" Media");
+        }
+        else if (AZ::StringFunc::EndsWith(request.m_sourceFile, Audio::Wwise::BankExtension))
+        {
+            jobKey.append(" Bank");
+        }
+
+        for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
+        {
+            // If there are no platform mappings (i.e. there was no config file), we want to
+            // process the job anyways.
+            bool createJob = m_wwiseConfig.m_platformMappings.empty();
+
+            // If the config file was parsed, need to filter out jobs that don't apply.
+            for (const auto& platformConfig : m_wwiseConfig.m_platformMappings)
+            {
+                // Check if the job request should go through.
+                if (info.m_identifier == platformConfig.m_assetPlatform
+                    || info.m_identifier == platformConfig.m_altAssetPlatform)
+                {
+                    AZStd::string sourceFile(request.m_sourceFile);
+                    AZStd::string_view banksPath(Audio::Wwise::DefaultBanksPath);
+                    if (AZ::StringFunc::StartsWith(sourceFile, banksPath))
+                    {
+                        // Remove the leading banks path from the source file...
+                        AZ::StringFunc::RKeep(sourceFile, banksPath.length(), true);
+                    }
+
+                    // If the source file now begins with the right Wwise platform folder, create the job...
+                    if (AZ::StringFunc::StartsWith(sourceFile, platformConfig.m_wwisePlatform, true))
+                    {
+                        createJob = true;
+                        break;
+                    }
+                }
+            }
+
+            if (createJob)
+            {
+                AssetBuilderSDK::JobDescriptor descriptor;
+                descriptor.m_jobKey = jobKey;
+                descriptor.m_critical = true;
+                descriptor.SetPlatformIdentifier(info.m_identifier.c_str());
+                descriptor.m_priority = 0;
+                response.m_createJobOutputs.push_back(descriptor);
+            }
+        }
+
+        response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
+    }
+
+    // The request will contain the CreateJobResponse you constructed earlier, including any keys and
+    // values you placed into the hash table
+    void WwiseBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
+    {
+        AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Starting Job.\n");
+        AZ::IO::PathView fullPath(request.m_fullPath);
+
+        if (m_isShuttingDown)
+        {
+            AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str());
+            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
+            return;
+        }
+        else
+        {
+            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
+            AssetBuilderSDK::JobProduct jobProduct(request.m_fullPath);
+
+            // if the file is a bnk
+            AZ::IO::PathView requestExtension = fullPath.Extension();
+            if (requestExtension.Native() == Audio::Wwise::BankExtension)
+            {
+                AssetBuilderSDK::ProductPathDependencySet dependencyPaths;
+
+                // Push assets back into the response's product list
+                // Assets you created in your temp path can be specified using paths relative to the temp path
+                // since that is assumed where you're writing stuff.
+                AZ::Outcome<AZStd::string, AZStd::string> gatherProductDependenciesResponse = GatherProductDependencies(request.m_fullPath, request.m_sourceFile, dependencyPaths);
+                if (!gatherProductDependenciesResponse.IsSuccess())
+                {
+                    AZ_Error(WwiseBuilderWindowName, false, "Dependency gathering for %s failed. %s",
+                        request.m_fullPath.c_str(), gatherProductDependenciesResponse.GetError().c_str());
+                }
+                else
+                {
+                    if (!gatherProductDependenciesResponse.GetValue().empty())
+                    {
+                        AZ_Warning(WwiseBuilderWindowName, false, "%s", gatherProductDependenciesResponse.GetValue().c_str());
+                    }
+                    jobProduct.m_pathDependencies = AZStd::move(dependencyPaths);
+                }
+            }
+
+            response.m_outputProducts.push_back(jobProduct);
+        }
+    }
+
+    AZ::Outcome<AZStd::string, AZStd::string> WwiseBuilderWorker::GatherProductDependencies(const AZStd::string& fullPath, const AZStd::string& relativePath, AssetBuilderSDK::ProductPathDependencySet& dependencies)
+    {
+        AZ::IO::Path bankMetadataPath(fullPath);
+        bankMetadataPath.ReplaceExtension(Internal::SoundbankDependencyFileExtension);
+        AZ::IO::Path relativeSoundsPath(relativePath, AZ::IO::PosixPathSeparator);
+        relativeSoundsPath.RemoveFilename();
+        AZStd::string success_message;
+
+        // Look for the corresponding .bankdeps file next to the bank itself.
+        if (!AZ::IO::SystemFile::Exists(bankMetadataPath.c_str()))
+        {
+            // If this is the init bank, skip it. Otherwise, register the init bank as a dependency, and warn that a full
+            //  dependency graph can't be created without a .bankdeps file for the bank.
+            AZ::IO::PathView requestFileName = AZ::IO::PathView(fullPath).Filename();
+            if (requestFileName != Audio::Wwise::InitBank)
+            {
+                success_message = AZStd::string::format(
+                    "Failed to find the metadata file %s for soundbank %s. Full dependency information cannot be determined without the "
+                    "metadata file. Please regenerate the metadata for this soundbank.",
+                    bankMetadataPath.c_str(), fullPath.c_str());
+            }
+            return AZ::Success(success_message);
+        }
+
+        AZ::u64 fileSize = AZ::IO::SystemFile::Length(bankMetadataPath.c_str());
+        if (fileSize == 0)
+        {
+            return AZ::Failure(AZStd::string::format(
+                "Soundbank metadata file at path %s is an empty file. Please regenerate the metadata for this soundbank.",
+                bankMetadataPath.c_str()));
+        }
+
+        AZStd::vector<char> buffer(fileSize + 1);
+        buffer[fileSize] = 0;
+        if (!AZ::IO::SystemFile::Read(bankMetadataPath.c_str(), buffer.data()))
+        {
+            return AZ::Failure(AZStd::string::format(
+                "Failed to read the soundbank metadata file at path %s. Please make sure the file is not open or being edited by another "
+                "program.",
+                bankMetadataPath.c_str()));
+        }
+
+        // load the file
+        rapidjson::Document bankMetadataDoc;
+        bankMetadataDoc.Parse(buffer.data());
+        if (bankMetadataDoc.GetParseError() != rapidjson::ParseErrorCode::kParseErrorNone)
+        {
+            return AZ::Failure(AZStd::string::format(
+                "Failed to parse soundbank metadata at path %s into JSON. Please regenerate the metadata for this soundbank.",
+                bankMetadataPath.c_str()));
+        }
+
+        AZStd::vector<AZStd::string> wwiseFiles;
+        AZ::Outcome<AZStd::string, AZStd::string> gatherDependenciesResult = Internal::GetDependenciesFromMetadata(bankMetadataDoc, wwiseFiles);
+        if (!gatherDependenciesResult.IsSuccess())
+        {
+            return AZ::Failure(AZStd::string::format(
+                "Dependency metadata file %s was processed, with errors:\n%s", bankMetadataPath.c_str(),
+                gatherDependenciesResult.GetError().c_str()));
+        }
+        else if (!gatherDependenciesResult.GetValue().empty())
+        {
+            success_message = AZStd::string::format(
+                "Dependency metadata file %s was processed, with warnings:\n%s", bankMetadataPath.c_str(),
+                gatherDependenciesResult.GetValue().c_str());
+        }
+
+        // Register dependencies stored in the file to the job response. (they'll be relative to the bank itself.)
+        for (const AZStd::string& wwiseFile : wwiseFiles)
+        {
+            dependencies.emplace((relativeSoundsPath / wwiseFile).Native(), AssetBuilderSDK::ProductPathDependencyType::ProductFile);
+        }
+
+        return AZ::Success(success_message);
+    }
+
+} // namespace WwiseBuilder

+ 46 - 0
Gems/AudioEngineWwise/Code/Source/Builder/WwiseBuilderWorker.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/parallel/atomic.h>
+#include <AssetBuilderSDK/AssetBuilderBusses.h>
+#include <AssetBuilderSDK/AssetBuilderSDK.h>
+
+#include <Config_wwise.h>
+
+namespace WwiseBuilder
+{
+    //! Wwise Builder is responsible for processing encoded audio media such as sound banks
+    class WwiseBuilderWorker
+        : public AssetBuilderSDK::AssetBuilderCommandBus::Handler
+    {
+    public:
+        AZ_RTTI(WwiseBuilderWorker, "{85224E40-9211-4C05-9397-06E056470171}");
+
+        WwiseBuilderWorker();
+        ~WwiseBuilderWorker() = default;
+
+        //! Asset Builder Callback Functions
+        void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response);
+        void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
+
+        //! AssetBuilderSDK::AssetBuilderCommandBus interface
+        void ShutDown() override;
+
+        AZ::Outcome<AZStd::string, AZStd::string> GatherProductDependencies(const AZStd::string& fullPath, const AZStd::string& relativePath, AssetBuilderSDK::ProductPathDependencySet& dependencies);
+
+    private:
+        void Initialize();
+
+        AZStd::atomic_bool m_isShuttingDown;
+        bool m_initialized = false;
+        Audio::Wwise::ConfigurationSettings m_wwiseConfig;
+    };
+
+} // WwiseBuilder

+ 19 - 0
Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemControl_wwise.cpp

@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#include <AudioSystemControl_wwise.h>
+
+namespace AudioControls
+{
+    //-------------------------------------------------------------------------------------------//
+    IAudioSystemControl_wwise::IAudioSystemControl_wwise(const AZStd::string& name, CID id, TImplControlType type)
+        : IAudioSystemControl(name, id, type)
+    {
+    }
+} // namespace AudioControls

+ 40 - 0
Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemControl_wwise.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#pragma once
+
+#include <IAudioInterfacesCommonData.h>
+
+#include <IAudioSystemControl.h>
+
+namespace AudioControls
+{
+    enum EWwiseControlTypes
+    {
+        eWCT_INVALID = 0,
+        eWCT_WWISE_EVENT = AUDIO_BIT(0),
+        eWCT_WWISE_RTPC = AUDIO_BIT(1),
+        eWCT_WWISE_SWITCH = AUDIO_BIT(2),
+        eWCT_WWISE_AUX_BUS = AUDIO_BIT(3),
+        eWCT_WWISE_SOUND_BANK = AUDIO_BIT(4),
+        eWCT_WWISE_GAME_STATE = AUDIO_BIT(5),
+        eWCT_WWISE_SWITCH_GROUP = AUDIO_BIT(6),
+        eWCT_WWISE_GAME_STATE_GROUP = AUDIO_BIT(7),
+    };
+
+    //-------------------------------------------------------------------------------------------//
+    class IAudioSystemControl_wwise
+        : public IAudioSystemControl
+    {
+    public:
+        IAudioSystemControl_wwise() {}
+        IAudioSystemControl_wwise(const AZStd::string& name, CID id, TImplControlType type);
+        ~IAudioSystemControl_wwise() override {}
+    };
+} // namespace AudioControls

+ 636 - 0
Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp

@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#include <AudioSystemEditor_wwise.h>
+
+#include <AzCore/Math/Crc.h>
+#include <AzCore/std/smart_ptr/make_shared.h>
+#include <AzCore/std/string/conversions.h>
+#include <AzCore/StringFunc/StringFunc.h>
+#include <AzCore/Utils/Utils.h>
+
+#include <ACETypes.h>
+#include <AudioSystemControl_wwise.h>
+#include <Common_wwise.h>
+
+#include <QDir>
+
+void InitWwiseResources()
+{
+    Q_INIT_RESOURCE(EditorWwise);
+}
+
+namespace AudioControls
+{
+    //-------------------------------------------------------------------------------------------//
+    TImplControlType TagToType(const AZStd::string_view tag)
+    {
+        if (tag == Audio::WwiseXmlTags::WwiseEventTag)
+        {
+            return eWCT_WWISE_EVENT;
+        }
+        else if (tag == Audio::WwiseXmlTags::WwiseRtpcTag)
+        {
+            return eWCT_WWISE_RTPC;
+        }
+        else if (tag == Audio::WwiseXmlTags::WwiseAuxBusTag)
+        {
+            return eWCT_WWISE_AUX_BUS;
+        }
+        else if (tag == Audio::WwiseXmlTags::WwiseFileTag)
+        {
+            return eWCT_WWISE_SOUND_BANK;
+        }
+        else if (tag == Audio::WwiseXmlTags::WwiseSwitchTag)
+        {
+            return eWCT_WWISE_SWITCH_GROUP;
+        }
+        else if (tag == Audio::WwiseXmlTags::WwiseStateTag)
+        {
+            return eWCT_WWISE_GAME_STATE_GROUP;
+        }
+        return eWCT_INVALID;
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    const AZStd::string_view TypeToTag(const TImplControlType type)
+    {
+        switch (type)
+        {
+        case eWCT_WWISE_EVENT:
+            return Audio::WwiseXmlTags::WwiseEventTag;
+        case eWCT_WWISE_RTPC:
+            return Audio::WwiseXmlTags::WwiseRtpcTag;
+        case eWCT_WWISE_SWITCH:
+            return Audio::WwiseXmlTags::WwiseValueTag;
+        case eWCT_WWISE_AUX_BUS:
+            return Audio::WwiseXmlTags::WwiseAuxBusTag;
+        case eWCT_WWISE_SOUND_BANK:
+            return Audio::WwiseXmlTags::WwiseFileTag;
+        case eWCT_WWISE_GAME_STATE:
+            return Audio::WwiseXmlTags::WwiseValueTag;
+        case eWCT_WWISE_SWITCH_GROUP:
+            return Audio::WwiseXmlTags::WwiseSwitchTag;
+        case eWCT_WWISE_GAME_STATE_GROUP:
+            return Audio::WwiseXmlTags::WwiseStateTag;
+        }
+        return "";
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    CAudioSystemEditor_wwise::CAudioSystemEditor_wwise()
+    {
+        InitWwiseResources();
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    void CAudioSystemEditor_wwise::Reload()
+    {
+        // set all the controls as placeholder as we don't know if
+        // any of them have been removed but still have connections to them
+        for (const auto& idControlPair : m_controls)
+        {
+            TControlPtr control = idControlPair.second;
+            if (control)
+            {
+                control->SetPlaceholder(true);
+            }
+        }
+
+        // reload data
+        m_loader.Load(this);
+
+        m_connectionsByID.clear();
+        UpdateConnectedStatus();
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    IAudioSystemControl* CAudioSystemEditor_wwise::CreateControl(const SControlDef& controlDefinition)
+    {
+        AZStd::string fullName = controlDefinition.m_name;
+        IAudioSystemControl* parent = controlDefinition.m_parentControl;
+        if (parent)
+        {
+            AZ::StringFunc::Path::Join(controlDefinition.m_parentControl->GetName().c_str(), fullName.c_str(), fullName);
+        }
+
+        if (!controlDefinition.m_path.empty())
+        {
+            AZ::StringFunc::Path::Join(controlDefinition.m_path.c_str(), fullName.c_str(), fullName);
+        }
+
+        CID id = GetID(fullName);
+
+        IAudioSystemControl* control = GetControl(id);
+        if (control)
+        {
+            if (control->IsPlaceholder())
+            {
+                control->SetPlaceholder(false);
+                if (parent && parent->IsPlaceholder())
+                {
+                    parent->SetPlaceholder(false);
+                }
+            }
+            return control;
+        }
+        else
+        {
+            TControlPtr newControl = AZStd::make_shared<IAudioSystemControl_wwise>(controlDefinition.m_name, id, controlDefinition.m_type);
+            if (!parent)
+            {
+                parent = &m_rootControl;
+            }
+
+            parent->AddChild(newControl.get());
+            newControl->SetParent(parent);
+            newControl->SetLocalized(controlDefinition.m_isLocalized);
+            m_controls[id] = newControl;
+            return newControl.get();
+        }
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    IAudioSystemControl* CAudioSystemEditor_wwise::GetControl(CID id) const
+    {
+        if (id != ACE_INVALID_CID)
+        {
+            auto it = m_controls.find(id);
+            if (it != m_controls.end())
+            {
+                return it->second.get();
+            }
+        }
+        return nullptr;
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    IAudioSystemControl* CAudioSystemEditor_wwise::GetControlByName(AZStd::string name, bool isLocalized, IAudioSystemControl* parent) const
+    {
+        if (parent)
+        {
+            AZ::StringFunc::Path::Join(parent->GetName().c_str(), name.c_str(), name);
+        }
+
+        if (isLocalized)
+        {
+            AZ::StringFunc::Path::Join(m_loader.GetLocalizationFolder().c_str(), name.c_str(), name);
+        }
+        return GetControl(GetID(name));
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionToControl(EACEControlType atlControlType, IAudioSystemControl* middlewareControl)
+    {
+        if (middlewareControl)
+        {
+            middlewareControl->SetConnected(true);
+            ++m_connectionsByID[middlewareControl->GetId()];
+
+            if (middlewareControl->GetType() == eWCT_WWISE_RTPC)
+            {
+                switch (atlControlType)
+                {
+                    case EACEControlType::eACET_RTPC:
+                    {
+                        return AZStd::make_shared<CRtpcConnection>(middlewareControl->GetId());
+                    }
+                    case EACEControlType::eACET_SWITCH_STATE:
+                    {
+                        return AZStd::make_shared<CStateToRtpcConnection>(middlewareControl->GetId());
+                    }
+                }
+            }
+
+            return AZStd::make_shared<IAudioConnection>(middlewareControl->GetId());
+        }
+        return nullptr;
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(AZ::rapidxml::xml_node<char>* node, EACEControlType atlControlType)
+    {
+        if (node)
+        {
+            AZStd::string_view element(node->name());
+            TImplControlType type = TagToType(element);
+            if (type != AUDIO_IMPL_INVALID_TYPE)
+            {
+                AZStd::string name;
+                AZStd::string_view localized;
+
+                if (auto nameAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false);
+                    nameAttr != nullptr)
+                {
+                    name = nameAttr->value();
+                }
+
+                if (auto localizedAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseLocalizedAttribute, 0, false);
+                    localizedAttr != nullptr)
+                {
+                    localized = localizedAttr->value();
+                }
+
+                bool isLocalized = AZ::StringFunc::Equal(localized, "true");
+
+                // If the control wasn't found, create a placeholder.
+                // We want to see that connection even if it's not in the middleware.
+                // User could be viewing the editor without a middleware project.
+                IAudioSystemControl* control = GetControlByName(name, isLocalized);
+                if (!control)
+                {
+                    control = CreateControl(SControlDef(name, type));
+                    if (control)
+                    {
+                        control->SetPlaceholder(true);
+                        control->SetLocalized(isLocalized);
+                    }
+                }
+
+                // If it's a switch we connect to one of the states within the switch
+                if (type == eWCT_WWISE_SWITCH_GROUP || type == eWCT_WWISE_GAME_STATE_GROUP)
+                {
+                    if (auto childNode = node->first_node();
+                        childNode != nullptr)
+                    {
+                        AZStd::string childName;
+                        if (auto childNameAttr = childNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false);
+                            childNameAttr != nullptr)
+                        {
+                            childName = childNameAttr->value();
+                        }
+
+                        IAudioSystemControl* childControl = GetControlByName(childName, false, control);
+                        if (!childControl)
+                        {
+                            childControl = CreateControl(SControlDef(
+                                childName, type == eWCT_WWISE_SWITCH_GROUP ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, control));
+                        }
+                        control = childControl;
+                    }
+                }
+
+                if (control)
+                {
+                    control->SetConnected(true);
+                    ++m_connectionsByID[control->GetId()];
+
+                    if (type == eWCT_WWISE_RTPC)
+                    {
+                        switch (atlControlType)
+                        {
+                            case EACEControlType::eACET_RTPC:
+                            {
+                                TRtpcConnectionPtr connection = AZStd::make_shared<CRtpcConnection>(control->GetId());
+
+                                float mult = 1.0f;
+                                float shift = 0.0f;
+
+                                if (auto multAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseMutiplierAttribute, 0, false);
+                                    multAttr != nullptr)
+                                {
+                                    mult = AZStd::stof(AZStd::string(multAttr->value()));
+                                }
+
+                                if (auto shiftAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseShiftAttribute, 0, false);
+                                    shiftAttr != nullptr)
+                                {
+                                    shift = AZStd::stof(AZStd::string(shiftAttr->value()));
+                                }
+
+                                connection->m_mult = mult;
+                                connection->m_shift = shift;
+                                return connection;
+                            }
+                            case EACEControlType::eACET_SWITCH_STATE:
+                            {
+                                TStateConnectionPtr connection = AZStd::make_shared<CStateToRtpcConnection>(control->GetId());
+
+                                float value = 0.0f;
+                                if (auto valueAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseValueAttribute, 0, false);
+                                    valueAttr != nullptr)
+                                {
+                                    value = AZStd::stof(AZStd::string(valueAttr->value()));
+                                }
+
+                                connection->m_value = value;
+                                return connection;
+                            }
+                            case EACEControlType::eACET_ENVIRONMENT:
+                            {
+                                return AZStd::make_shared<IAudioConnection>(control->GetId());
+                            }
+                        }
+                    }
+                    else
+                    {
+                        return AZStd::make_shared<IAudioConnection>(control->GetId());
+                    }
+                }
+            }
+        }
+        return nullptr;
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    AZ::rapidxml::xml_node<char>* CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType)
+    {
+        const IAudioSystemControl* control = GetControl(connection->GetID());
+        if (control)
+        {
+            XmlAllocator& xmlAllocator(AudioControls::s_xmlAllocator);
+
+            switch (control->GetType())
+            {
+                case AudioControls::eWCT_WWISE_SWITCH:
+                    [[fallthrough]];
+                case AudioControls::eWCT_WWISE_SWITCH_GROUP:
+                    [[fallthrough]];
+                case AudioControls::eWCT_WWISE_GAME_STATE:
+                    [[fallthrough]];
+                case AudioControls::eWCT_WWISE_GAME_STATE_GROUP:
+                {
+                    const IAudioSystemControl* parent = control->GetParent();
+                    if (parent)
+                    {
+                        AZStd::string_view parentType = TypeToTag(parent->GetType());
+                        auto switchNode = xmlAllocator.allocate_node(
+                            AZ::rapidxml::node_element,
+                            xmlAllocator.allocate_string(parentType.data())
+                        );
+
+                        auto switchNameAttr = xmlAllocator.allocate_attribute(
+                            Audio::WwiseXmlTags::WwiseNameAttribute,
+                            xmlAllocator.allocate_string(parent->GetName().c_str())
+                        );
+
+                        auto stateNode = xmlAllocator.allocate_node(
+                            AZ::rapidxml::node_element,
+                            Audio::WwiseXmlTags::WwiseValueTag
+                        );
+
+                        auto stateNameAttr = xmlAllocator.allocate_attribute(
+                            Audio::WwiseXmlTags::WwiseNameAttribute,
+                            xmlAllocator.allocate_string(control->GetName().c_str())
+                        );
+
+                        switchNode->append_attribute(switchNameAttr);
+                        stateNode->append_attribute(stateNameAttr);
+                        switchNode->append_node(stateNode);
+                        return switchNode;
+                    }
+                    break;
+                }
+
+                case AudioControls::eWCT_WWISE_RTPC:
+                {
+                    auto connectionNode = xmlAllocator.allocate_node(
+                        AZ::rapidxml::node_element,
+                        xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
+                    );
+
+                    auto nameAttr = xmlAllocator.allocate_attribute(
+                        Audio::WwiseXmlTags::WwiseNameAttribute,
+                        xmlAllocator.allocate_string(control->GetName().c_str())
+                    );
+
+                    connectionNode->append_attribute(nameAttr);
+
+                    if (atlControlType == eACET_RTPC)
+                    {
+                        AZStd::shared_ptr<const CRtpcConnection> rtpcConnection = AZStd::static_pointer_cast<const CRtpcConnection>(connection);
+                        if (rtpcConnection->m_mult != 1.f)
+                        {
+                            auto multAttr = xmlAllocator.allocate_attribute(
+                                Audio::WwiseXmlTags::WwiseMutiplierAttribute,
+                                xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_mult).c_str())
+                            );
+
+                            connectionNode->append_attribute(multAttr);
+                        }
+
+                        if (rtpcConnection->m_shift != 0.f)
+                        {
+                            auto shiftAttr = xmlAllocator.allocate_attribute(
+                                Audio::WwiseXmlTags::WwiseShiftAttribute,
+                                xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_shift).c_str())
+                            );
+
+                            connectionNode->append_attribute(shiftAttr);
+                        }
+                    }
+                    else if (atlControlType == eACET_SWITCH_STATE)
+                    {
+                        AZStd::shared_ptr<const CStateToRtpcConnection> stateConnection = AZStd::static_pointer_cast<const CStateToRtpcConnection>(connection);
+
+                        auto valueAttr = xmlAllocator.allocate_attribute(
+                            Audio::WwiseXmlTags::WwiseValueAttribute,
+                            xmlAllocator.allocate_string(AZStd::to_string(stateConnection->m_value).c_str())
+                        );
+
+                        connectionNode->append_attribute(valueAttr);
+                    }
+
+                    return connectionNode;
+                }
+
+                case AudioControls::eWCT_WWISE_EVENT:
+                    [[fallthrough]];
+                case AudioControls::eWCT_WWISE_AUX_BUS:
+                {
+                    auto connectionNode = xmlAllocator.allocate_node(
+                        AZ::rapidxml::node_element,
+                        xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
+                    );
+
+                    auto nameAttr = xmlAllocator.allocate_attribute(
+                        Audio::WwiseXmlTags::WwiseNameAttribute,
+                        xmlAllocator.allocate_string(control->GetName().c_str())
+                    );
+
+                    connectionNode->append_attribute(nameAttr);
+                    return connectionNode;
+                }
+
+                case AudioControls::eWCT_WWISE_SOUND_BANK:
+                {
+                    auto connectionNode = xmlAllocator.allocate_node(
+                        AZ::rapidxml::node_element,
+                        xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
+                    );
+
+                    auto nameAttr = xmlAllocator.allocate_attribute(
+                        Audio::WwiseXmlTags::WwiseNameAttribute,
+                        xmlAllocator.allocate_string(control->GetName().c_str())
+                    );
+
+                    connectionNode->append_attribute(nameAttr);
+
+                    if (control->IsLocalized())
+                    {
+                        auto locAttr = xmlAllocator.allocate_attribute(
+                            Audio::WwiseXmlTags::WwiseLocalizedAttribute,
+                            xmlAllocator.allocate_string("true")
+                        );
+
+                        connectionNode->append_attribute(locAttr);
+                    }
+
+                    return connectionNode;
+                }
+            }
+        }
+        return nullptr;
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    const AZStd::string_view CAudioSystemEditor_wwise::GetTypeIcon(TImplControlType type) const
+    {
+        switch (type)
+        {
+        case eWCT_WWISE_EVENT:
+            return ":/Editor/WwiseIcons/event_nor.svg";
+        case eWCT_WWISE_RTPC:
+            return ":/Editor/WwiseIcons/gameparameter_nor.svg";
+        case eWCT_WWISE_SWITCH:
+            return ":/Editor/WwiseIcons/switch_nor.svg";
+        case eWCT_WWISE_AUX_BUS:
+            return ":/Editor/WwiseIcons/auxbus_nor.svg";
+        case eWCT_WWISE_SOUND_BANK:
+            return ":/Editor/WwiseIcons/soundbank_nor.svg";
+        case eWCT_WWISE_GAME_STATE:
+            return ":/Editor/WwiseIcons/state_nor.svg";
+        case eWCT_WWISE_SWITCH_GROUP:
+            return ":/Editor/WwiseIcons/switchgroup_nor.svg";
+        case eWCT_WWISE_GAME_STATE_GROUP:
+            return ":/Editor/WwiseIcons/stategroup_nor.svg";
+        default:
+            // should make a "default"/empty icon...
+            return ":/Editor/WwiseIcons/switchgroup_nor.svg";
+        }
+    }
+
+    const AZStd::string_view CAudioSystemEditor_wwise::GetTypeIconSelected(TImplControlType type) const
+    {
+        switch (type)
+        {
+        case eWCT_WWISE_EVENT:
+            return ":/Editor/WwiseIcons/event_nor_hover.svg";
+        case eWCT_WWISE_RTPC:
+            return ":/Editor/WwiseIcons/gameparameter_nor_hover.svg";
+        case eWCT_WWISE_SWITCH:
+            return ":/Editor/WwiseIcons/switch_nor_hover.svg";
+        case eWCT_WWISE_AUX_BUS:
+            return ":/Editor/WwiseIcons/auxbus_nor_hover.svg";
+        case eWCT_WWISE_SOUND_BANK:
+            return ":/Editor/WwiseIcons/soundbank_nor_hover.svg";
+        case eWCT_WWISE_GAME_STATE:
+            return ":/Editor/WwiseIcons/state_nor_hover.svg";
+        case eWCT_WWISE_SWITCH_GROUP:
+            return ":/Editor/WwiseIcons/switchgroup_nor_hover.svg";
+        case eWCT_WWISE_GAME_STATE_GROUP:
+            return ":/Editor/WwiseIcons/stategroup_nor_hover.svg";
+        default:
+            // should make a "default"/empty icon...
+            return ":/Editor/WwiseIcons/switchgroup_nor_hover.svg";
+        }
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    EACEControlType CAudioSystemEditor_wwise::ImplTypeToATLType(TImplControlType type) const
+    {
+        switch (type)
+        {
+        case eWCT_WWISE_EVENT:
+            return eACET_TRIGGER;
+        case eWCT_WWISE_RTPC:
+            return eACET_RTPC;
+        case eWCT_WWISE_SWITCH:
+        case eWCT_WWISE_GAME_STATE:
+            return eACET_SWITCH_STATE;
+        case eWCT_WWISE_AUX_BUS:
+            return eACET_ENVIRONMENT;
+        case eWCT_WWISE_SOUND_BANK:
+            return eACET_PRELOAD;
+        case eWCT_WWISE_GAME_STATE_GROUP:
+        case eWCT_WWISE_SWITCH_GROUP:
+            return eACET_SWITCH;
+        }
+        return eACET_NUM_TYPES;
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    TImplControlTypeMask CAudioSystemEditor_wwise::GetCompatibleTypes(EACEControlType atlControlType) const
+    {
+        switch (atlControlType)
+        {
+        case eACET_TRIGGER:
+            return eWCT_WWISE_EVENT;
+        case eACET_RTPC:
+            return eWCT_WWISE_RTPC;
+        case eACET_SWITCH:
+            return (eWCT_WWISE_SWITCH | eWCT_WWISE_GAME_STATE);
+        case eACET_SWITCH_STATE:
+            return (eWCT_WWISE_SWITCH | eWCT_WWISE_GAME_STATE | eWCT_WWISE_RTPC);
+        case eACET_ENVIRONMENT:
+            return (eWCT_WWISE_AUX_BUS | eWCT_WWISE_RTPC);
+        case eACET_PRELOAD:
+            return eWCT_WWISE_SOUND_BANK;
+        }
+        return AUDIO_IMPL_INVALID_TYPE;
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    CID CAudioSystemEditor_wwise::GetID(const AZStd::string_view name) const
+    {
+        return Audio::AudioStringToID<CID>(name.data());
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    AZStd::string CAudioSystemEditor_wwise::GetName() const
+    {
+        return "Wwise";
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    void CAudioSystemEditor_wwise::UpdateConnectedStatus()
+    {
+        for (const auto& idCountPair : m_connectionsByID)
+        {
+            if (idCountPair.second > 0)
+            {
+                IAudioSystemControl* control = GetControl(idCountPair.first);
+                if (control)
+                {
+                    control->SetConnected(true);
+                }
+            }
+        }
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    void CAudioSystemEditor_wwise::ConnectionRemoved(IAudioSystemControl* control)
+    {
+        int connectionCount = m_connectionsByID[control->GetId()] - 1;
+        if (connectionCount <= 0)
+        {
+            connectionCount = 0;
+            control->SetConnected(false);
+        }
+        m_connectionsByID[control->GetId()] = connectionCount;
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    AZ::IO::FixedMaxPath CAudioSystemEditor_wwise::GetDataPath() const
+    {
+        auto projectPath = AZ::IO::FixedMaxPath{ AZ::Utils::GetProjectPath() };
+        return (projectPath / "sounds" / "wwise_project");
+    }
+
+} // namespace AudioControls

+ 110 - 0
Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h

@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#pragma once
+
+#include <IAudioSystemEditor.h>
+#include <IAudioConnection.h>
+#include <IAudioSystemControl.h>
+#include <AudioWwiseLoader.h>
+
+namespace AudioControls
+{
+    //-------------------------------------------------------------------------------------------//
+    class CRtpcConnection
+        : public IAudioConnection
+    {
+    public:
+        explicit CRtpcConnection(CID id)
+            : IAudioConnection(id)
+            , m_mult(1.0f)
+            , m_shift(0.0f)
+        {}
+
+        ~CRtpcConnection() override = default;
+
+        bool HasProperties() override { return true; }
+
+        float m_mult;
+        float m_shift;
+    };
+
+    using TRtpcConnectionPtr = AZStd::shared_ptr<CRtpcConnection>;
+
+    //-------------------------------------------------------------------------------------------//
+    class CStateToRtpcConnection
+        : public IAudioConnection
+    {
+    public:
+        explicit CStateToRtpcConnection(CID id)
+            : IAudioConnection(id)
+            , m_value(0.0f)
+        {}
+
+        ~CStateToRtpcConnection() override = default;
+
+        bool HasProperties() override { return true; }
+
+        float m_value;
+    };
+
+    using TStateConnectionPtr = AZStd::shared_ptr<CStateToRtpcConnection>;
+
+
+    //-------------------------------------------------------------------------------------------//
+    class CAudioSystemEditor_wwise
+        : public IAudioSystemEditor
+    {
+        friend class CAudioWwiseLoader;
+
+    public:
+        CAudioSystemEditor_wwise();
+        ~CAudioSystemEditor_wwise() override = default;
+
+        //////////////////////////////////////////////////////////
+        // IAudioSystemEditor implementation
+        /////////////////////////////////////////////////////////
+        void Reload() override;
+        IAudioSystemControl* CreateControl(const SControlDef& controlDefinition) override;
+        IAudioSystemControl* GetRoot() override { return &m_rootControl; }
+        IAudioSystemControl* GetControl(CID id) const override;
+        EACEControlType ImplTypeToATLType(TImplControlType type) const override;
+        TImplControlTypeMask GetCompatibleTypes(EACEControlType atlControlType) const override;
+        TConnectionPtr CreateConnectionToControl(EACEControlType atlControlType, IAudioSystemControl* middlewareControl) override;
+        TConnectionPtr CreateConnectionFromXMLNode(AZ::rapidxml::xml_node<char>* node, EACEControlType atlControlType) override;
+        AZ::rapidxml::xml_node<char>* CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) override;
+        const AZStd::string_view GetTypeIcon(TImplControlType type) const override;
+        const AZStd::string_view GetTypeIconSelected(TImplControlType type) const override;
+        AZStd::string GetName() const override;
+        AZ::IO::FixedMaxPath GetDataPath() const override;
+        void DataSaved() override {}
+        void ConnectionRemoved(IAudioSystemControl* control) override;
+        //////////////////////////////////////////////////////////
+
+    private:
+        IAudioSystemControl* GetControlByName(AZStd::string name, bool isLocalized = false, IAudioSystemControl* parent = nullptr) const;
+
+        // Gets the ID of the control given its name. As controls can have the same name
+        // if they're under different parents, the name of the parent is also needed (if there is one)
+        CID GetID(const AZStd::string_view name) const;
+
+        void UpdateConnectedStatus();
+
+        IAudioSystemControl m_rootControl;
+
+        using TControlPtr = AZStd::shared_ptr<IAudioSystemControl>;
+        using TControlMap = AZStd::unordered_map<CID, TControlPtr>;
+        TControlMap m_controls;
+
+        using TConnectionsMap = AZStd::unordered_map<CID, int>;
+        TConnectionsMap m_connectionsByID;
+        CAudioWwiseLoader m_loader;
+    };
+
+} // namespace AudioControls

+ 185 - 0
Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp

@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#include <AudioWwiseLoader.h>
+
+#include <IAudioSystemControl.h>
+#include <IAudioSystemEditor.h>
+#include <AudioSystemEditor_wwise.h>
+#include <AudioFileUtils.h>
+#include <Config_wwise.h>
+
+
+namespace AudioControls
+{
+    namespace WwiseStrings
+    {
+        // Wwise Project Folders
+        static constexpr const char GameParametersFolder[] = "Game Parameters";
+        static constexpr const char GameStatesFolder[] = "States";
+        static constexpr const char SwitchesFolder[] = "Switches";
+        static constexpr const char EventsFolder[] = "Events";
+        static constexpr const char EnvironmentsFolder[] = "Master-Mixer Hierarchy";
+
+        // Wwise Xml Tags
+        static constexpr const char GameParameterTag[] = "GameParameter";
+        static constexpr const char EventTag[] = "Event";
+        static constexpr const char AuxBusTag[] = "AuxBus";
+        static constexpr const char SwitchGroupTag[] = "SwitchGroup";
+        static constexpr const char StateGroupTag[] = "StateGroup";
+        static constexpr const char ChildrenListTag[] = "ChildrenList";
+        static constexpr const char NameAttribute[] = "Name";
+
+    } // namespace WwiseStrings
+
+    //-------------------------------------------------------------------------------------------//
+    void CAudioWwiseLoader::Load(CAudioSystemEditor_wwise* audioSystemImpl)
+    {
+        m_audioSystemImpl = audioSystemImpl;
+        const AZ::IO::FixedMaxPath wwiseProjectFullPath{ m_audioSystemImpl->GetDataPath() };
+        LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::GameParametersFolder }.Native());
+        LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::GameStatesFolder }.Native());
+        LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::SwitchesFolder }.Native());
+        LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::EventsFolder }.Native());
+        LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::EnvironmentsFolder }.Native());
+        LoadSoundBanks(Audio::Wwise::GetBanksRootPath(), "", false);
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    void CAudioWwiseLoader::LoadSoundBanks(const AZStd::string_view rootFolder, const AZStd::string_view subPath, bool isLocalized)
+    {
+        AZ::IO::FixedMaxPath searchPath(rootFolder);
+        searchPath /= subPath;
+        auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*");
+        bool isLocalizedLoaded = isLocalized;
+
+        for (const auto& filePath : foundFiles)
+        {
+            AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", filePath.c_str());
+            AZ::IO::PathView fileName = filePath.Filename();
+
+            if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str()))
+            {
+                if (fileName != Audio::Wwise::ExternalSourcesPath && !isLocalizedLoaded)
+                {
+                    // each sub-folder represents a different language,
+                    // we load only one as all of them should have the
+                    // same content (in the future we want to have a
+                    // consistency report to highlight if this is not the case)
+                    m_localizationFolder.assign(fileName.Native().data(), fileName.Native().size());
+                    LoadSoundBanks(searchPath.Native(), m_localizationFolder, true);
+                    isLocalizedLoaded = true;
+                }
+            }
+            else if (fileName.Extension() == Audio::Wwise::BankExtension && fileName != Audio::Wwise::InitBank)
+            {
+                m_audioSystemImpl->CreateControl(
+                    SControlDef(AZStd::string{ fileName.Native() }, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath));
+            }
+        }
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    void CAudioWwiseLoader::LoadControlsInFolder(const AZStd::string_view folderPath)
+    {
+        auto foundFiles = Audio::FindFilesInPath(folderPath, "*");
+
+        for (const auto& filePath : foundFiles)
+        {
+            AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", filePath.c_str());
+
+            if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str()))
+            {
+                LoadControlsInFolder(filePath.Native());
+            }
+            else
+            {
+                // Open the file, read into an xmlDoc, and call LoadControls with the root xml node...
+                AZ_TracePrintf("AudioWwiseLoader", "Loading Xml from '%s'", filePath.c_str());
+
+                Audio::ScopedXmlLoader xmlFileLoader(filePath.Native());
+                if (!xmlFileLoader.HasError())
+                {
+                    LoadControl(xmlFileLoader.GetRootNode());
+                }
+            }
+        }
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    void CAudioWwiseLoader::ExtractControlsFromXML(const AZ::rapidxml::xml_node<char>* xmlNode, EWwiseControlTypes type, const AZStd::string_view controlTag, const AZStd::string_view controlNameAttribute)
+    {
+        AZStd::string_view xmlTag(xmlNode->name());
+        if (xmlTag == controlTag)
+        {
+            if (auto nameAttr = xmlNode->first_attribute(controlNameAttribute.data()))
+            {
+                m_audioSystemImpl->CreateControl(SControlDef(nameAttr->value(), type));
+            }
+        }
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    void CAudioWwiseLoader::LoadControl(const AZ::rapidxml::xml_node<char>* xmlNode)
+    {
+        if (!xmlNode)
+        {
+            return;
+        }
+
+        ExtractControlsFromXML(xmlNode, eWCT_WWISE_RTPC, WwiseStrings::GameParameterTag, WwiseStrings::NameAttribute);
+        ExtractControlsFromXML(xmlNode, eWCT_WWISE_EVENT, WwiseStrings::EventTag, WwiseStrings::NameAttribute);
+        ExtractControlsFromXML(xmlNode, eWCT_WWISE_AUX_BUS, WwiseStrings::AuxBusTag, WwiseStrings::NameAttribute);
+
+        AZStd::string_view xmlTag(xmlNode->name());
+        bool isSwitchTag = (xmlTag == WwiseStrings::SwitchGroupTag);
+        bool isStateTag = (xmlTag == WwiseStrings::StateGroupTag);
+
+        if (isSwitchTag || isStateTag)
+        {
+            if (auto nameAttr = xmlNode->first_attribute(WwiseStrings::NameAttribute))
+            {
+                const AZStd::string parentName(nameAttr->value());
+                IAudioSystemControl* group = m_audioSystemImpl->GetControlByName(parentName);
+                if (!group)
+                {
+                    group = m_audioSystemImpl->CreateControl(SControlDef(parentName, isSwitchTag ? eWCT_WWISE_SWITCH_GROUP : eWCT_WWISE_GAME_STATE_GROUP));
+                }
+
+                auto childrenNode = xmlNode->first_node(WwiseStrings::ChildrenListTag);
+                if (childrenNode)
+                {
+                    auto childNode = childrenNode->first_node();
+                    while (childNode)
+                    {
+                        if (auto childNameAttr = childNode->first_attribute(WwiseStrings::NameAttribute))
+                        {
+                            m_audioSystemImpl->CreateControl(SControlDef(childNameAttr->value(), isSwitchTag ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, group));
+                        }
+                        childNode = childNode->next_sibling();
+                    }
+                }
+            }
+        }
+
+        auto childNode = xmlNode->first_node();
+        while (childNode)
+        {
+            LoadControl(childNode);
+            childNode = childNode->next_sibling();
+        }
+    }
+
+    //-------------------------------------------------------------------------------------------//
+    const AZStd::string& CAudioWwiseLoader::GetLocalizationFolder() const
+    {
+        return m_localizationFolder;
+    }
+
+} // namespace AudioControls

+ 40 - 0
Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#pragma once
+
+#include <ACETypes.h>
+#include <AzCore/std/string/string_view.h>
+#include <AudioSystemControl_wwise.h>
+
+#include <AzCore/XML/rapidxml.h>
+
+namespace AudioControls
+{
+    class CAudioSystemEditor_wwise;
+
+    //-------------------------------------------------------------------------------------------//
+    class CAudioWwiseLoader
+    {
+    public:
+        CAudioWwiseLoader() = default;
+        void Load(CAudioSystemEditor_wwise* audioSystemImpl);
+        const AZStd::string& GetLocalizationFolder() const;
+
+    private:
+        void LoadSoundBanks(const AZStd::string_view rootFolder, const AZStd::string_view subPath, bool isLocalized);
+        void LoadControlsInFolder(const AZStd::string_view folderPath);
+        void LoadControl(const AZ::rapidxml::xml_node<char>* xmlNode);
+        void ExtractControlsFromXML(const AZ::rapidxml::xml_node<char>* xmlNode, EWwiseControlTypes type, const AZStd::string_view controlTag, const AZStd::string_view controlNameAttribute);
+
+    private:
+        AZStd::string m_localizationFolder;
+        CAudioSystemEditor_wwise* m_audioSystemImpl = nullptr;
+    };
+} // namespace AudioControls

+ 20 - 0
Gems/AudioEngineWwise/Code/Source/Editor/EditorWwise.qrc

@@ -0,0 +1,20 @@
+<RCC>
+  <qresource prefix="/Editor/WwiseIcons">
+    <file alias="auxbus_nor.svg">WwiseIcons/auxbus_nor.svg</file>
+    <file alias="auxbus_nor_hover.svg">WwiseIcons/auxbus_nor_hover.svg</file>
+    <file alias="event_nor.svg">WwiseIcons/event_nor.svg</file>
+    <file alias="event_nor_hover.svg">WwiseIcons/event_nor_hover.svg</file>
+    <file alias="gameparameter_nor.svg">WwiseIcons/gameparameter_nor.svg</file>
+    <file alias="gameparameter_nor_hover.svg">WwiseIcons/gameparameter_nor_hover.svg</file>
+    <file alias="soundbank_nor.svg">WwiseIcons/soundbank_nor.svg</file>
+    <file alias="soundbank_nor_hover.svg">WwiseIcons/soundbank_nor_hover.svg</file>
+    <file alias="state_nor.svg">WwiseIcons/state_nor.svg</file>
+    <file alias="state_nor_hover.svg">WwiseIcons/state_nor_hover.svg</file>
+    <file alias="stategroup_nor.svg">WwiseIcons/stategroup_nor.svg</file>
+    <file alias="stategroup_nor_hover.svg">WwiseIcons/stategroup_nor_hover.svg</file>
+    <file alias="switch_nor.svg">WwiseIcons/switch_nor.svg</file>
+    <file alias="switch_nor_hover.svg">WwiseIcons/switch_nor_hover.svg</file>
+    <file alias="switchgroup_nor.svg">WwiseIcons/switchgroup_nor.svg</file>
+    <file alias="switchgroup_nor_hover.svg">WwiseIcons/switchgroup_nor_hover.svg</file>
+  </qresource>
+</RCC>

+ 26 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/auxbus_nor.svg

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / auxbus_nor</title>
+    <g id="icon-/-General-/-WWise-controls-/-auxbus_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <g id="Group" transform="translate(2.500000, 2.500000)" fill="#FFFFFF">
+            <rect id="Rectangle-Copy-65" x="7.85015167" y="4.85500591" width="3" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-68" transform="translate(1.500000, 1.650384) rotate(-90.000000) translate(-1.500000, -1.650384) " x="0" y="1" width="3" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-73" transform="translate(3.497744, 1.650384) rotate(-90.000000) translate(-3.497744, -1.650384) " x="1.99774393" y="1" width="3" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-74" fill-opacity="0.5" transform="translate(1.500000, 3.805871) rotate(-90.000000) translate(-1.500000, -3.805871) " x="0.844513191" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-75" fill-opacity="0.5" transform="translate(3.497744, 3.805871) rotate(-90.000000) translate(-3.497744, -3.805871) " x="2.84225712" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-76" fill-opacity="0.5" transform="translate(5.495488, 3.805871) rotate(-90.000000) translate(-5.495488, -3.805871) " x="4.84000105" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-77" transform="translate(5.495488, 1.650384) rotate(-90.000000) translate(-5.495488, -1.650384) " x="3.99548786" y="1" width="3" height="1.30076785"></rect>
+            <polygon id="Rectangle-Copy-72" points="0.850151669 4.15044404 6.85015167 4.15044404 6.85015167 10.150444 3.850212 10.150444 0.850151669 7.25011322"></polygon>
+            <rect id="Rectangle-Copy-66" x="7.85015167" y="6.83454936" width="3" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-69" fill-opacity="0.5" x="6.53917805" y="4.85888821" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-70" fill-opacity="0.5" x="6.53917805" y="6.83454936" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-71" fill-opacity="0.5" x="6.53917805" y="8.84967619" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-67" x="7.85015167" y="8.84967619" width="3" height="1.30076785"></rect>
+        </g>
+    </g>
+</svg>

+ 26 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/auxbus_nor_hover.svg

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / auxbus_nor - hover</title>
+    <g id="icon-/-General-/-WWise-controls-/-auxbus_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <g id="Group" transform="translate(2.500000, 2.500000)" fill="#FFFFFF">
+            <rect id="Rectangle-Copy-65" x="7.85015167" y="4.85500591" width="3" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-68" transform="translate(1.500000, 1.650384) rotate(-90.000000) translate(-1.500000, -1.650384) " x="0" y="1" width="3" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-73" transform="translate(3.497744, 1.650384) rotate(-90.000000) translate(-3.497744, -1.650384) " x="1.99774393" y="1" width="3" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-74" fill-opacity="0.5" transform="translate(1.500000, 3.805871) rotate(-90.000000) translate(-1.500000, -3.805871) " x="0.844513191" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-75" fill-opacity="0.5" transform="translate(3.497744, 3.805871) rotate(-90.000000) translate(-3.497744, -3.805871) " x="2.84225712" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-76" fill-opacity="0.5" transform="translate(5.495488, 3.805871) rotate(-90.000000) translate(-5.495488, -3.805871) " x="4.84000105" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-77" transform="translate(5.495488, 1.650384) rotate(-90.000000) translate(-5.495488, -1.650384) " x="3.99548786" y="1" width="3" height="1.30076785"></rect>
+            <polygon id="Rectangle-Copy-72" points="0.850151669 4.15044404 6.85015167 4.15044404 6.85015167 10.150444 3.850212 10.150444 0.850151669 7.25011322"></polygon>
+            <rect id="Rectangle-Copy-66" x="7.85015167" y="6.83454936" width="3" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-69" fill-opacity="0.5" x="6.53917805" y="4.85888821" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-70" fill-opacity="0.5" x="6.53917805" y="6.83454936" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-71" fill-opacity="0.5" x="6.53917805" y="8.84967619" width="1.31097362" height="1.30076785"></rect>
+            <rect id="Rectangle-Copy-67" x="7.85015167" y="8.84967619" width="3" height="1.30076785"></rect>
+        </g>
+    </g>
+</svg>

+ 21 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/event_nor.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
+    <title>WWise controls icon </title>
+    <desc>Created with Sketch.</desc>
+    <g id="WWise-controls-icon-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
+        <g id="Group" transform="translate(1.500000, 4.000000)" fill="#FFFFFF">
+            <rect id="Rectangle" x="0" y="1.09528159" width="2.19056317" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-65" x="9.93618454" y="1.09528159" width="2.19056317" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-72" x="3.2944383" y="0" width="5.53787113" height="7.67747148"></rect>
+            <rect id="Rectangle-Copy-66" x="9.93618454" y="3.27125666" width="2.19056317" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-68" fill-opacity="0.5" x="2.19056317" y="1.09528159" width="1.10387512" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-69" fill-opacity="0.5" x="8.83230942" y="1.09528159" width="1.10387512" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-70" fill-opacity="0.5" x="8.83230942" y="3.27125666" width="1.10387512" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-71" fill-opacity="0.5" x="8.83230942" y="5.44723173" width="1.10387512" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-67" x="9.93618454" y="5.44723173" width="2.19056317" height="1.09528159"></rect>
+        </g>
+    </g>
+</svg>

+ 21 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/event_nor_hover.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
+    <title>WWise controls icon  hover</title>
+    <desc>Created with Sketch.</desc>
+    <g id="WWise-controls-icon--hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
+        <g id="Group" transform="translate(1.500000, 4.000000)" fill="#FFFFFF">
+            <rect id="Rectangle" x="0" y="1.09528159" width="2.19056317" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-65" x="9.93618454" y="1.09528159" width="2.19056317" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-72" x="3.2944383" y="0" width="5.53787113" height="7.67747148"></rect>
+            <rect id="Rectangle-Copy-66" x="9.93618454" y="3.27125666" width="2.19056317" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-68" fill-opacity="0.5" x="2.19056317" y="1.09528159" width="1.10387512" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-69" fill-opacity="0.5" x="8.83230942" y="1.09528159" width="1.10387512" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-70" fill-opacity="0.5" x="8.83230942" y="3.27125666" width="1.10387512" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-71" fill-opacity="0.5" x="8.83230942" y="5.44723173" width="1.10387512" height="1.09528159"></rect>
+            <rect id="Rectangle-Copy-67" x="9.93618454" y="5.44723173" width="2.19056317" height="1.09528159"></rect>
+        </g>
+    </g>
+</svg>

+ 12 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/gameparameter_nor.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / gameparameter_nor</title>
+    <g id="icon-/-General-/-WWise-controls-/-gameparameter_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <path d="M9.49786987,3.50689788 L9.497,4.38189788 L14.4704694,4.38189788 L14.4704694,5.63189788 L9.497,5.63189788 L9.49786987,6.50689788 L8.625,6.50689788 L8.625,9.46089788 L9.5,9.46186067 L9.5,12.4618607 L6.5,12.4618607 L6.5,11.5858979 L1.375,11.5868607 L1.375,10.3368607 L6.5,10.3358979 L6.5,9.46186067 L7.375,9.46089788 L7.375,6.50689788 L6.49786987,6.50689788 L6.49786987,3.50689788 L9.49786987,3.50689788 Z" id="Combined-Shape" fill="#FFFFFF"></path>
+    </g>
+</svg>

+ 12 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/gameparameter_nor_hover.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / gameparameter_nor - hover</title>
+    <g id="icon-/-General-/-WWise-controls-/-gameparameter_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <path d="M9.49786987,3.50689788 L9.497,4.38189788 L14.4704694,4.38189788 L14.4704694,5.63189788 L9.497,5.63189788 L9.49786987,6.50689788 L8.625,6.50689788 L8.625,9.46089788 L9.5,9.46186067 L9.5,12.4618607 L6.5,12.4618607 L6.5,11.5858979 L1.375,11.5868607 L1.375,10.3368607 L6.5,10.3358979 L6.5,9.46186067 L7.375,9.46089788 L7.375,6.50689788 L6.49786987,6.50689788 L6.49786987,3.50689788 L9.49786987,3.50689788 Z" id="Combined-Shape" fill="#FFFFFF"></path>
+    </g>
+</svg>

ファイルの差分が大きいため隠しています
+ 18 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/soundbank_nor.svg


ファイルの差分が大きいため隠しています
+ 18 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/soundbank_nor_hover.svg


+ 18 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/state_nor.svg

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / state_nor</title>
+    <defs>
+        <path d="M7.97092008,3.98274536 C7.96690592,4.36330533 7.97319095,5.36748326 7.98977517,8.01719254 C7.97410642,10.5206355 7.9768247,11.5552026 7.97635312,11.9827454 C10.1830707,11.975596 11.9697467,10.1874775 11.9697467,7.98274536 C11.9697467,5.7762181 10.1801373,3.98697455 7.97092008,3.98274536 L7.97092008,3.98274536 Z M8,13.5 C4.96243388,13.5 2.5,11.0375661 2.5,8 C2.5,4.96243388 4.96243388,2.5 8,2.5 C11.0375661,2.5 13.5,4.96243388 13.5,8 C13.5,11.0375661 11.0375661,13.5 8,13.5 Z" id="path-1"></path>
+    </defs>
+    <g id="icon-/-General-/-WWise-controls-/-state_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <mask id="mask-2" fill="white">
+            <use xlink:href="#path-1"></use>
+        </mask>
+        <use id="Shape" fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-1"></use>
+    </g>
+</svg>

+ 18 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/state_nor_hover.svg

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / state_nor - hover</title>
+    <defs>
+        <path d="M7.97092008,3.98274536 C7.96690592,4.36330533 7.97319095,5.36748326 7.98977517,8.01719254 C7.97410642,10.5206355 7.9768247,11.5552026 7.97635312,11.9827454 C10.1830707,11.975596 11.9697467,10.1874775 11.9697467,7.98274536 C11.9697467,5.7762181 10.1801373,3.98697455 7.97092008,3.98274536 L7.97092008,3.98274536 Z M8,13.5 C4.96243388,13.5 2.5,11.0375661 2.5,8 C2.5,4.96243388 4.96243388,2.5 8,2.5 C11.0375661,2.5 13.5,4.96243388 13.5,8 C13.5,11.0375661 11.0375661,13.5 8,13.5 Z" id="path-1"></path>
+    </defs>
+    <g id="icon-/-General-/-WWise-controls-/-state_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <mask id="mask-2" fill="white">
+            <use xlink:href="#path-1"></use>
+        </mask>
+        <use id="Shape" fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-1"></use>
+    </g>
+</svg>

+ 16 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/stategroup_nor.svg

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / stategroup_nor</title>
+    <g id="icon-/-General-/-WWise-controls-/-stategroup_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <g id="Group" transform="translate(1.500000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero">
+            <path d="M6.49154389,1 C6.48953681,1.19027999 6.49267932,1.69236895 6.50097143,3.01722359 C6.49313705,4.26894508 6.4944962,4.78622861 6.49426041,5 C7.59761917,4.9964253 8.49095719,4.10236608 8.49095719,3 C8.49095719,1.89673637 7.59615251,1.00211459 6.49154389,1 L6.49154389,1 Z M6.49095719,6 C4.83410294,6 3.49095719,4.65685425 3.49095719,3 C3.49095719,1.34314575 4.83410294,0 6.49095719,0 C8.14781143,0 9.49095719,1.34314575 9.49095719,3 C9.49095719,4.65685425 8.14781143,6 6.49095719,6 Z" id="Shape-Copy"></path>
+            <path d="M9.49154389,7 C9.48953681,7.19027999 9.49267932,7.69236895 9.50097143,9.01722359 C9.49313705,10.2689451 9.4944962,10.7862286 9.49426041,11 C10.5976192,10.9964253 11.4909572,10.1023661 11.4909572,9 C11.4909572,7.89673637 10.5961525,7.00211459 9.49154389,7 L9.49154389,7 Z M9.49095719,12 C7.83410294,12 6.49095719,10.6568542 6.49095719,9 C6.49095719,7.34314575 7.83410294,6 9.49095719,6 C11.1478114,6 12.4909572,7.34314575 12.4909572,9 C12.4909572,10.6568542 11.1478114,12 9.49095719,12 Z" id="Shape-Copy-2"></path>
+            <path d="M3.0005867,7 C2.99857962,7.19027999 3.00172214,7.69236895 3.01001424,9.01722359 C3.00217987,10.2689451 3.00353901,10.7862286 3.00330322,11 C4.10666199,10.9964253 5,10.1023661 5,9 C5,7.89673637 4.10519532,7.00211459 3.0005867,7 L3.0005867,7 Z M3,12 C1.34314575,12 0,10.6568542 0,9 C0,7.34314575 1.34314575,6 3,6 C4.65685425,6 6,7.34314575 6,9 C6,10.6568542 4.65685425,12 3,12 Z" id="Shape-Copy-3"></path>
+        </g>
+    </g>
+</svg>

+ 16 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/stategroup_nor_hover.svg

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / stategroup_nor - hover</title>
+    <g id="icon-/-General-/-WWise-controls-/-stategroup_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <g id="Group" transform="translate(1.500000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero">
+            <path d="M6.49154389,1 C6.48953681,1.19027999 6.49267932,1.69236895 6.50097143,3.01722359 C6.49313705,4.26894508 6.4944962,4.78622861 6.49426041,5 C7.59761917,4.9964253 8.49095719,4.10236608 8.49095719,3 C8.49095719,1.89673637 7.59615251,1.00211459 6.49154389,1 L6.49154389,1 Z M6.49095719,6 C4.83410294,6 3.49095719,4.65685425 3.49095719,3 C3.49095719,1.34314575 4.83410294,0 6.49095719,0 C8.14781143,0 9.49095719,1.34314575 9.49095719,3 C9.49095719,4.65685425 8.14781143,6 6.49095719,6 Z" id="Shape-Copy"></path>
+            <path d="M9.49154389,7 C9.48953681,7.19027999 9.49267932,7.69236895 9.50097143,9.01722359 C9.49313705,10.2689451 9.4944962,10.7862286 9.49426041,11 C10.5976192,10.9964253 11.4909572,10.1023661 11.4909572,9 C11.4909572,7.89673637 10.5961525,7.00211459 9.49154389,7 L9.49154389,7 Z M9.49095719,12 C7.83410294,12 6.49095719,10.6568542 6.49095719,9 C6.49095719,7.34314575 7.83410294,6 9.49095719,6 C11.1478114,6 12.4909572,7.34314575 12.4909572,9 C12.4909572,10.6568542 11.1478114,12 9.49095719,12 Z" id="Shape-Copy-2"></path>
+            <path d="M3.0005867,7 C2.99857962,7.19027999 3.00172214,7.69236895 3.01001424,9.01722359 C3.00217987,10.2689451 3.00353901,10.7862286 3.00330322,11 C4.10666199,10.9964253 5,10.1023661 5,9 C5,7.89673637 4.10519532,7.00211459 3.0005867,7 L3.0005867,7 Z M3,12 C1.34314575,12 0,10.6568542 0,9 C0,7.34314575 1.34314575,6 3,6 C4.65685425,6 6,7.34314575 6,9 C6,10.6568542 4.65685425,12 3,12 Z" id="Shape-Copy-3"></path>
+        </g>
+    </g>
+</svg>

+ 12 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/switch_nor.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / switch_nor</title>
+    <g id="icon-/-General-/-WWise-controls-/-switch_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <path d="M12.5,3.5 L12.5,12.5 L3.5,12.5 L3.5,3.5 L12.5,3.5 Z M10.5,5.5 L5.5,5.5 L5.5,10.5 L10.5,10.5 L10.5,5.5 Z" id="Combined-Shape-Copy-2" fill="#FFFFFF"></path>
+    </g>
+</svg>

+ 12 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/switch_nor_hover.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / switch_nor - hover</title>
+    <g id="icon-/-General-/-WWise-controls-/-switch_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <path d="M12.5,3.5 L12.5,12.5 L3.5,12.5 L3.5,3.5 L12.5,3.5 Z M10.5,5.5 L5.5,5.5 L5.5,10.5 L10.5,10.5 L10.5,5.5 Z" id="Combined-Shape-Copy-2" fill="#FFFFFF"></path>
+    </g>
+</svg>

+ 12 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/switchgroup_nor.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / switchgroup_nor</title>
+    <g id="icon-/-General-/-WWise-controls-/-switchgroup_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <path d="M10.5,10.5 L10.5,13.5 L7.5,13.5 L7.5,10.5 L10.5,10.5 Z M6.5,2.5 L6.5,13.5 L5.25,13.5 L5.25,2.5 L6.5,2.5 Z M9.5,11.5 L8.5,11.5 L8.5,12.5 L9.5,12.5 L9.5,11.5 Z M10.5,6.5 L10.5,9.5 L7.5,9.5 L7.5,6.5 L10.5,6.5 Z M9.5,7.5 L8.5,7.5 L8.5,8.5 L9.5,8.5 L9.5,7.5 Z M10.5,2.5 L10.5,5.5 L7.5,5.5 L7.5,2.5 L10.5,2.5 Z M9.5,3.5 L8.5,3.5 L8.5,4.5 L9.5,4.5 L9.5,3.5 Z" id="Combined-Shape" fill="#FFFFFF"></path>
+    </g>
+</svg>

+ 12 - 0
Gems/AudioEngineWwise/Code/Source/Editor/WwiseIcons/switchgroup_nor_hover.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
+    <title>icon / General / WWise controls / switchgroup_nor - hover</title>
+    <g id="icon-/-General-/-WWise-controls-/-switchgroup_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
+        <g id="Icons-/-main-window-/-WWise-controls-">
+            <rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
+            <rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
+        </g>
+        <path d="M10.5,10.5 L10.5,13.5 L7.5,13.5 L7.5,10.5 L10.5,10.5 Z M6.5,2.5 L6.5,13.5 L5.25,13.5 L5.25,2.5 L6.5,2.5 Z M9.5,11.5 L8.5,11.5 L8.5,12.5 L9.5,12.5 L9.5,11.5 Z M10.5,6.5 L10.5,9.5 L7.5,9.5 L7.5,6.5 L10.5,6.5 Z M9.5,7.5 L8.5,7.5 L8.5,8.5 L9.5,8.5 L9.5,7.5 Z M10.5,2.5 L10.5,5.5 L7.5,5.5 L7.5,2.5 L10.5,2.5 Z M9.5,3.5 L8.5,3.5 L8.5,4.5 L9.5,4.5 L9.5,3.5 Z" id="Combined-Shape" fill="#FFFFFF"></path>
+    </g>
+</svg>

+ 211 - 0
Gems/AudioEngineWwise/Code/Source/Engine/ATLEntities_wwise.h

@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#pragma once
+
+#include <ATLEntityData.h>
+#include <IAudioInterfacesCommonData.h>
+#include <AudioAllocators.h>
+
+#include <AzCore/std/containers/map.h>
+#include <AzCore/std/containers/vector.h>
+
+#include <AK/SoundEngine/Common/AkTypes.h>
+#include <AK/AkWwiseSDKVersion.h>
+
+namespace Audio
+{
+    using TAKUniqueIDVector = AZStd::vector<AkUniqueID, Audio::AudioImplStdAllocator>;
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    struct SATLAudioObjectData_wwise
+        : public IATLAudioObjectData
+    {
+        // convert to ATLMapLookupType
+        using TEnvironmentImplMap = AZStd::map<AkAuxBusID, float, AZStd::less<AkAuxBusID>, Audio::AudioImplStdAllocator>;
+
+        SATLAudioObjectData_wwise(const AkGameObjectID nPassedAKID, const bool bPassedHasPosition)
+            : bNeedsToUpdateEnvironments(false)
+            , bHasPosition(bPassedHasPosition)
+            , nAKID(nPassedAKID)
+        {}
+
+        ~SATLAudioObjectData_wwise() override {}
+
+        bool bNeedsToUpdateEnvironments;
+        const bool bHasPosition;
+        const AkGameObjectID nAKID;
+        TEnvironmentImplMap cEnvironmentImplAmounts;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    struct SATLListenerData_wwise
+        : public IATLListenerData
+    {
+        explicit SATLListenerData_wwise(const AkGameObjectID passedObjectId)
+            : nAKListenerObjectId(passedObjectId)
+        {}
+
+        ~SATLListenerData_wwise() override {}
+
+        const AkGameObjectID nAKListenerObjectId = AK_INVALID_GAME_OBJECT;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    struct SATLTriggerImplData_wwise
+        : public IATLTriggerImplData
+    {
+        explicit SATLTriggerImplData_wwise(const AkUniqueID nPassedAKID)
+            : nAKID(nPassedAKID)
+        {}
+
+        ~SATLTriggerImplData_wwise() override {}
+
+        const AkUniqueID nAKID;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    struct SATLRtpcImplData_wwise
+        : public IATLRtpcImplData
+    {
+        SATLRtpcImplData_wwise(const AkRtpcID nPassedAKID, const float m_fPassedMult, const float m_fPassedShift)
+            : m_fMult(m_fPassedMult)
+            , m_fShift(m_fPassedShift)
+            , nAKID(nPassedAKID)
+        {}
+
+        ~SATLRtpcImplData_wwise() override {}
+
+        const float m_fMult;
+        const float m_fShift;
+        const AkRtpcID nAKID;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    enum EWwiseSwitchType : TATLEnumFlagsType
+    {
+        eWST_NONE   = 0,
+        eWST_SWITCH = 1,
+        eWST_STATE  = 2,
+        eWST_RTPC   = 3,
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    struct SATLSwitchStateImplData_wwise
+        : public IATLSwitchStateImplData
+    {
+        SATLSwitchStateImplData_wwise(
+            const EWwiseSwitchType ePassedType,
+            const AkUInt32 nPassedAKSwitchID,
+            const AkUInt32 nPassedAKStateID,
+            const float fPassedRtpcValue = 0.0f)
+            : eType(ePassedType)
+            , nAKSwitchID(nPassedAKSwitchID)
+            , nAKStateID(nPassedAKStateID)
+            , fRtpcValue(fPassedRtpcValue)
+        {}
+
+        ~SATLSwitchStateImplData_wwise() override {}
+
+        const EWwiseSwitchType eType;
+        const AkUInt32 nAKSwitchID;
+        const AkUInt32 nAKStateID;
+        const float fRtpcValue;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    enum EWwiseAudioEnvironmentType : TATLEnumFlagsType
+    {
+        eWAET_NONE      = 0,
+        eWAET_AUX_BUS   = 1,
+        eWAET_RTPC      = 2,
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    struct SATLEnvironmentImplData_wwise
+        : public IATLEnvironmentImplData
+    {
+        explicit SATLEnvironmentImplData_wwise(const EWwiseAudioEnvironmentType ePassedType)
+            : eType(ePassedType)
+        {}
+
+        SATLEnvironmentImplData_wwise(const EWwiseAudioEnvironmentType ePassedType, const AkAuxBusID nPassedAKBusID)
+            : eType(ePassedType)
+            , nAKBusID(nPassedAKBusID)
+        {
+            AZ_Assert(ePassedType == eWAET_AUX_BUS, "SATLEnvironmentImplData_wwise - type is incorrect, expected an Aux Bus!");
+        }
+
+        SATLEnvironmentImplData_wwise(
+            const EWwiseAudioEnvironmentType ePassedType,
+            const AkRtpcID nPassedAKRtpcID,
+            const float fPassedMult,
+            const float fPassedShift)
+            : eType(ePassedType)
+            , nAKRtpcID(nPassedAKRtpcID)
+            , fMult(fPassedMult)
+            , fShift(fPassedShift)
+        {
+            AZ_Assert(ePassedType == eWAET_RTPC, "SATLEnvironmentImplData_wwise - type is incorrect, expected an RTPC!");
+        }
+
+        ~SATLEnvironmentImplData_wwise() override {}
+
+        const EWwiseAudioEnvironmentType eType;
+
+        union
+        {
+            // Aux Bus implementation
+            struct
+            {
+                AkAuxBusID nAKBusID;
+            };
+
+            // Rtpc implementation
+            struct
+            {
+                AkRtpcID nAKRtpcID;
+                float fMult;
+                float fShift;
+            };
+        };
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    struct SATLEventData_wwise
+        : public IATLEventData
+    {
+        explicit SATLEventData_wwise(const TAudioEventID nPassedID)
+            : audioEventState(eAES_NONE)
+            , nAKID(AK_INVALID_UNIQUE_ID)
+            , nATLID(nPassedID)
+            , nSourceId(INVALID_AUDIO_SOURCE_ID)
+        {}
+
+        ~SATLEventData_wwise() override {}
+
+        EAudioEventState audioEventState;
+        AkUniqueID nAKID;
+        const TAudioEventID nATLID;
+        TAudioSourceId nSourceId;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    struct SATLAudioFileEntryData_wwise
+        : public IATLAudioFileEntryData
+    {
+        SATLAudioFileEntryData_wwise()
+            : nAKBankID(AK_INVALID_BANK_ID)
+        {}
+
+        ~SATLAudioFileEntryData_wwise() override {}
+
+        AkBankID nAKBankID;
+    };
+} // namespace Audio

+ 220 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp

@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioInput/AudioInputFile.h>
+#include <AudioInput/WavParser.h>
+#include <Common_wwise.h>
+
+#include <AzCore/IO/FileIO.h>
+
+#include <AK/SoundEngine/Common/AkStreamMgrModule.h>
+
+namespace Audio
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Audio Input File
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioInputFile::AudioInputFile(const SAudioInputConfig& sourceConfig)
+    {
+        m_config = sourceConfig;
+
+        switch (sourceConfig.m_sourceType)
+        {
+        case AudioInputSourceType::WavFile:
+            m_parser.reset(aznew WavFileParser());
+            break;
+        case AudioInputSourceType::PcmFile:
+            break;
+        default:
+            return;
+        }
+
+        LoadFile();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioInputFile::~AudioInputFile()
+    {
+        UnloadFile();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    bool AudioInputFile::LoadFile()
+    {
+        bool result = false;
+
+        // Filename should be relative to the project assets root e.g.: 'sounds/files/my_sound.wav'
+        AZ::IO::FileIOStream fileStream(m_config.m_sourceFilename.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary);
+
+        if (fileStream.IsOpen())
+        {
+            m_dataSize = fileStream.GetLength();
+
+            if (m_dataSize > 0)
+            {
+                // Here if a parser is available, can pass the file stream forward
+                // so it can parse header information.
+                // It will return the number of header bytes read, that is an offset to
+                // the beginning of the real signal data.
+                if (m_parser)
+                {
+                    size_t headerBytesRead = m_parser->ParseHeader(fileStream);
+                    if (headerBytesRead > 0 && m_parser->IsHeaderValid())
+                    {
+                        // Update the size...
+                        m_dataSize = m_parser->GetDataSize();
+
+                        // Set the format configuration obtained from the file...
+                        m_config.m_bitsPerSample = m_parser->GetBitsPerSample();
+                        m_config.m_numChannels = m_parser->GetNumChannels();
+                        m_config.m_sampleRate = m_parser->GetSampleRate();
+                        m_config.m_sampleType = m_parser->GetSampleType();
+                    }
+                }
+
+
+                if (IsOk())
+                {
+                    // Allocate a new buffer to hold the data...
+                    m_dataPtr = new AZ::u8[m_dataSize];
+
+                    // Read file into internal buffer...
+                    size_t bytesRead = fileStream.Read(m_dataSize, m_dataPtr);
+
+                    ResetBookmarks();
+
+                    // Verify we read the full amount...
+                    result = (bytesRead == m_dataSize);
+                }
+            }
+
+            fileStream.Close();
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputFile::UnloadFile()
+    {
+        if (m_dataPtr)
+        {
+            delete [] m_dataPtr;
+            m_dataPtr = nullptr;
+        }
+        m_dataSize = 0;
+        m_dataCurrentPtr = nullptr;
+        m_dataCurrentReadSize = 0;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputFile::ReadInput([[maybe_unused]] const AudioStreamData& data)
+    {
+        // Don't really need this for File-based sources, the whole file is read in the constructor.
+        // However, we may need to implement this for asynchronous loading of the file (streaming).
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputFile::WriteOutput(AkAudioBuffer* akBuffer)
+    {
+        AZ::u16 numSampleFramesRequested = (akBuffer->MaxFrames() - akBuffer->uValidFrames);
+
+        if (m_config.m_sampleType == AudioInputSampleType::Int)
+        {
+            void* outBuffer = akBuffer->GetInterleavedData();
+
+            AZ::u16 numSampleFramesCopied = static_cast<AZ::u16>(CopyData(numSampleFramesRequested, outBuffer));
+
+            akBuffer->uValidFrames += numSampleFramesCopied;
+
+            akBuffer->eState = (numSampleFramesCopied > 0) ? AK_DataReady
+                : (IsEof() ? AK_NoMoreData : AK_NoDataReady);
+        }
+        else if (m_config.m_sampleType == AudioInputSampleType::Float)
+        {
+            // Not Implemented yet!
+            akBuffer->eState = AK_NoMoreData;
+
+            // Implementing this for files will likely involve de-interleaving the samples.
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    bool AudioInputFile::IsOk() const
+    {
+        bool ok = (m_dataSize > 0);
+        ok &= IsFormatValid();
+
+        if (m_parser)
+        {
+            ok &= m_parser->IsHeaderValid();
+            ok &= (m_dataSize == m_parser->GetDataSize());
+        }
+
+        return ok;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputFile::OnDeactivated()
+    {
+        if (m_config.m_autoUnloadFile)
+        {
+            UnloadFile();
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    size_t AudioInputFile::CopyData(size_t numSampleFrames, void* toBuffer)
+    {
+        // Copies data to an output buffer.
+        // Size requested is in sample frames, not bytes!
+        // Number of frames actually copied is returned.  This is useful if more
+        // frames were requested than can be copied.
+
+        if (!toBuffer || !numSampleFrames)
+        {
+            return 0;
+        }
+
+        const size_t frameBytes = (m_config.m_numChannels * m_config.m_bitsPerSample) >> 3;  // bits --> bytes
+        size_t copySize = numSampleFrames * frameBytes;
+
+        // Check if request is larger than remaining, trim off excess.
+        if (m_dataCurrentReadSize + copySize > m_dataSize)
+        {
+            size_t excess = (m_dataCurrentReadSize + copySize) - m_dataSize;
+            copySize -= excess;
+            numSampleFrames = (copySize / frameBytes);
+        }
+
+        if (copySize > 0)
+        {
+            ::memcpy(toBuffer, m_dataCurrentPtr, copySize);
+            m_dataCurrentReadSize += copySize;
+            m_dataCurrentPtr += copySize;
+        }
+
+        return numSampleFrames;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputFile::ResetBookmarks()
+    {
+        m_dataCurrentPtr = m_dataPtr;
+        m_dataCurrentReadSize = 0;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    bool AudioInputFile::IsEof() const
+    {
+        return (m_dataCurrentReadSize == m_dataSize);
+    }
+
+} // namespace Audio

+ 123 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.h

@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AudioSourceManager.h>
+
+namespace Audio
+{
+    /**
+     * Base class for audio file parser.
+     * Any supported audio file types will have a parser implementation
+     * that will parse header information to extract the audio format.
+     */
+    class AudioFileParser
+    {
+    public:
+        AUDIO_IMPL_CLASS_ALLOCATOR(AudioFileParser)
+
+        AudioFileParser() = default;
+        virtual ~AudioFileParser() = default;
+
+        AudioFileParser(const AudioFileParser&) = delete;
+        AudioFileParser& operator=(const AudioFileParser&) = delete;
+
+        /**
+         * Parse header from a file stream.
+         * Parses header of an audio file and returns the byte-offset into the file where the audio data begins.
+         * @param fileStream An opened file stream on the audio file.
+         * @return Byte-offset into the file where audio data begins.
+         */
+        virtual size_t ParseHeader(AZ::IO::FileIOStream& fileStream) = 0;
+
+        /**
+         * Check validity of the header info.
+         * This should only return true if the header was parsed and user can expect to see valid format data.
+         * @return True if the header was parsed without error.
+         */
+        virtual bool IsHeaderValid() const = 0;
+
+        virtual AudioInputSampleType GetSampleType() const = 0;
+        virtual AZ::u32 GetNumChannels() const = 0;
+        virtual AZ::u32 GetSampleRate() const = 0;
+        virtual AZ::u32 GetByteRate() const = 0;
+        virtual AZ::u32 GetBitsPerSample() const = 0;
+        virtual AZ::u32 GetDataSize() const = 0;
+    };
+
+
+    /**
+     * A type of AudioInputSource representing an audio file.
+     * Contains audio file data, holds a pointer to the raw data and provides methods to read chunks of data at a time
+     * to an output (AkAudioBuffer).
+     */
+    class AudioInputFile
+        : public AudioInputSource
+    {
+    public:
+        AUDIO_IMPL_CLASS_ALLOCATOR(AudioInputFile)
+
+        AudioInputFile(const SAudioInputConfig& sourceConfig);
+        ~AudioInputFile() override;
+
+        /**
+         * Load file into buffer.
+         * Use an AudioFileParser if needed to parse header information, then proceed to load the audio data
+         * to the internal buffer.
+         * @return True upon successful load, false otherwise.
+         */
+        bool LoadFile();
+
+        /**
+         * Unload the file data.
+         * Release the internal buffer of file data.
+         */
+        void UnloadFile();
+
+        void ReadInput(const AudioStreamData& data) override;
+        void WriteOutput(AkAudioBuffer* akBuffer) override;
+        bool IsOk() const override;
+
+        void OnDeactivated() override;
+
+        /**
+         * Copy data from the internal buffer to an output buffer.
+         * Copies a specified number of sample frames to an output buffer.  If more frames are requested
+         * than can be copied, only allowable frames are copied and number of frames that were copied is returned.
+         * @param numSampleFrames Number of sample frames requested for copy.
+         * @param toBuffer Output buffer to copy to.
+         * @return Number of sample frames actually copied.
+         */
+        size_t CopyData(size_t numSampleFrames, void* toBuffer);     // frames, not bytes!
+
+    private:
+        /**
+         * Resets internal bookmarking.
+         * Bookmarks are used internally to keep track of where we are in the buffer during
+         * chunk-copying to output.
+         */
+        void ResetBookmarks();
+
+        /**
+         * Checks whether data copying has reached the end of the file data.
+         * @return True if end of file has been reached, false otherwise.
+         */
+        bool IsEof() const;
+
+        AZStd::unique_ptr<AudioFileParser> m_parser = nullptr;
+
+        AZ::u8* m_dataPtr = nullptr;    ///< The internal data buffer.
+        size_t m_dataSize = 0;          ///< The internal data size.
+
+        // Bookmarks
+        AZ::u8* m_dataCurrentPtr = nullptr; ///< The internal bookmark pointer.
+        size_t m_dataCurrentReadSize = 0;   ///< The internal bookmark indicating how much data has been read so far.
+    };
+
+} // namespace Audio

+ 77 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputMicrophone.cpp

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioInput/AudioInputMicrophone.h>
+#include <AzCore/Casting/numeric_cast.h>
+#include <MicrophoneBus.h>
+
+namespace Audio
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Audio Input Source : Microphone
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioInputMicrophone::AudioInputMicrophone(const SAudioInputConfig& sourceConfig)
+    {
+        m_config = sourceConfig;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioInputMicrophone::~AudioInputMicrophone()
+    {
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputMicrophone::ReadInput([[maybe_unused]] const AudioStreamData& data)
+    {
+        // ReadInput only used when PUSHing source data in, and would need an internal buffer to store
+        // the data temporarily.  For Microphone, the microphone impl has its own internal buffer, so
+        // we only need to PULL data in WriteOutput.
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputMicrophone::WriteOutput(AkAudioBuffer* akBuffer)
+    {
+        AZ::u32 numSampleFramesRequested = (akBuffer->MaxFrames() - akBuffer->uValidFrames);
+
+        AkSampleType* channelData[2] = { nullptr, nullptr };
+
+        for (AZ::u32 channel = 0; channel < akBuffer->NumChannels(); ++channel)
+        {
+            channelData[channel] = akBuffer->GetChannel(channel);
+        }
+
+        size_t numSampleFramesCopied = 0;
+        MicrophoneRequestBus::BroadcastResult(numSampleFramesCopied, &MicrophoneRequestBus::Events::GetData, reinterpret_cast<void**>(channelData), numSampleFramesRequested, m_config, true);
+
+        akBuffer->uValidFrames += aznumeric_cast<AkUInt16>(numSampleFramesCopied);
+
+        akBuffer->eState = (numSampleFramesCopied > 0) ? AK_DataReady : AK_NoDataReady;
+        // handle the AK_NoMoreData condition?
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputMicrophone::OnDeactivated()
+    {
+        m_config.m_numChannels = 0;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    bool AudioInputMicrophone::IsOk() const
+    {
+        // Mono and Stereo only
+        bool ok = (m_config.m_numChannels == 1 || m_config.m_numChannels == 2);
+
+        // 32-bit float or 16-bit int only
+        ok &= (m_config.m_sampleType == AudioInputSampleType::Float && m_config.m_bitsPerSample == 32)
+            || (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample == 16);
+        return ok;
+    }
+
+} // namespace Audio

+ 30 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputMicrophone.h

@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AudioSourceManager.h>
+
+namespace Audio
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    class AudioInputMicrophone
+        : public AudioInputSource
+    {
+    public:
+        AudioInputMicrophone(const SAudioInputConfig& sourceConfig);
+        ~AudioInputMicrophone() override;
+
+        void ReadInput(const AudioStreamData& data) override;
+        void WriteOutput(AkAudioBuffer* akBuffer) override;
+        bool IsOk() const override;
+
+        void OnDeactivated() override;
+    };
+
+} // namespace Audio

+ 127 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp

@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioInput/AudioInputStream.h>
+#include <Common_wwise.h>
+#include <AzCore/Casting/numeric_cast.h>
+#include <AK/SoundEngine/Common/AkStreamMgrModule.h>
+
+namespace Audio
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Audio Streaming Input
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioInputStreaming::AudioInputStreaming(const SAudioInputConfig& sourceConfig)
+        : m_framesReady(0)
+    {
+        m_config = sourceConfig;
+
+        size_t bytesPerSample = (m_config.m_bitsPerSample >> 3);
+        size_t numSamples = m_config.m_sampleRate * m_config.m_numChannels;      // <-- This gives a 1 second buffer based on the configuration.
+
+        m_config.m_bufferSize = static_cast<AZ::u32>(numSamples * bytesPerSample);
+
+        if (m_config.m_sampleType == AudioInputSampleType::Float && m_config.m_bitsPerSample == 32)
+        {
+            m_buffer.reset(new RingBuffer<float>(numSamples));
+        }
+        else if (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample == 16)
+        {
+            m_buffer.reset(new RingBuffer<AZ::s16>(numSamples));
+        }
+        else
+        {
+            AZ_Error("AudioInputStreaming", false, "Audio Stream Format Unsupported!  Bits Per Sample = %d, Sample Type = %d",
+                m_config.m_bitsPerSample, static_cast<int>(m_config.m_sampleType));
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioInputStreaming::~AudioInputStreaming()
+    {
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    size_t AudioInputStreaming::ReadStreamingInput(const AudioStreamData& data)
+    {
+        size_t numFrames = data.m_sizeBytes / (m_config.m_bitsPerSample >> 3) / m_config.m_numChannels;
+        size_t framesAdded = m_buffer->AddData(data.m_data, numFrames, m_config.m_numChannels);
+        m_framesReady += framesAdded;
+        return framesAdded;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    size_t AudioInputStreaming::ReadStreamingMultiTrackInput(AudioStreamMultiTrackData& data)
+    {
+        size_t numFrames = data.m_sizeBytes / (m_config.m_bitsPerSample >> 3);
+        size_t framesAdded = m_buffer->AddMultiTrackDataInterleaved(data.m_data, numFrames, m_config.m_numChannels);
+        m_framesReady += framesAdded;
+        return framesAdded;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputStreaming::FlushStreamingInput()
+    {
+        m_buffer->ResetBuffer();
+        m_framesReady = 0;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    size_t AudioInputStreaming::GetStreamingInputNumFramesReady() const
+    {
+        return m_framesReady;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputStreaming::ReadInput([[maybe_unused]] const AudioStreamData& data)
+    {
+        // Intentionally left as an empty implementation.
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputStreaming::WriteOutput(AkAudioBuffer* akBuffer)
+    {
+        AZ::u16 numSampleFramesRequested = (akBuffer->MaxFrames() - akBuffer->uValidFrames);
+
+        AkSampleType* channelData[6] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
+
+        for (AZ::u32 channel = 0; channel < akBuffer->NumChannels(); ++channel)
+        {
+            channelData[channel] = akBuffer->GetChannel(channel);
+        }
+
+        bool deinterleave = (m_config.m_sampleType == AudioInputSampleType::Float);
+        size_t numSampleFramesCopied = m_buffer->ConsumeData(reinterpret_cast<void**>(channelData), numSampleFramesRequested, akBuffer->NumChannels(), deinterleave);
+        akBuffer->uValidFrames += aznumeric_cast<AkUInt16>(numSampleFramesCopied);
+        m_framesReady -= numSampleFramesCopied;
+
+        akBuffer->eState = (numSampleFramesCopied > 0) ? AK_DataReady : AK_NoDataReady;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    bool AudioInputStreaming::IsOk() const
+    {
+        return true;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputStreaming::OnActivated()
+    {
+        AZ_Assert(m_config.m_sourceId != INVALID_AUDIO_SOURCE_ID, "AudioInputStreaming - Being activated but no valid Source Id!\n");
+        AudioStreamingRequestBus::Handler::BusConnect(m_config.m_sourceId);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputStreaming::OnDeactivated()
+    {
+        AudioStreamingRequestBus::Handler::BusDisconnect();
+    }
+
+} // namespace Audio

+ 52 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.h

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include "AudioSourceManager.h"
+#include <IAudioSystem.h>
+#include <AudioRingBuffer.h>
+
+namespace Audio
+{
+    /**
+     * A type of AudioInputSource representing an audio stream.
+     * holds a buffer of the raw data and provides methods to read chunks of data at a time
+     * to an output (AkAudioBuffer).
+     */
+    class AudioInputStreaming
+        : public AudioInputSource
+        , public AudioStreamingRequestBus::Handler
+    {
+    public:
+        AUDIO_IMPL_CLASS_ALLOCATOR(AudioInputStreaming)
+
+        AudioInputStreaming(const SAudioInputConfig& sourceConfig);
+        ~AudioInputStreaming() override;
+
+        // AudioInputSource Interface
+        void ReadInput(const AudioStreamData& data) override;
+        void WriteOutput(AkAudioBuffer* akBuffer) override;
+        bool IsOk() const override;
+
+        void OnDeactivated() override;
+        void OnActivated() override;
+
+        // AudioStreamingRequestBus::Handler Interface
+        size_t ReadStreamingInput(const AudioStreamData& data) override;
+        size_t ReadStreamingMultiTrackInput(AudioStreamMultiTrackData& data) override;
+
+        void FlushStreamingInput();
+        size_t GetStreamingInputNumFramesReady() const;
+
+    private:
+        AZStd::unique_ptr<RingBufferBase> m_buffer = nullptr;
+        size_t m_framesReady;
+    };
+
+} // namespace Audio

+ 161 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/WavParser.cpp

@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioInput/WavParser.h>
+
+namespace Audio
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    /*static*/ const AZ::u8 WavFileParser::riff_tag[4] = { 'R', 'I', 'F', 'F' };
+    /*static*/ const AZ::u8 WavFileParser::wave_tag[4] = { 'W', 'A', 'V', 'E' };
+    /*static*/ const AZ::u8 WavFileParser::fmt__tag[4] = { 'f', 'm', 't', ' ' };
+    /*static*/ const AZ::u8 WavFileParser::data_tag[4] = { 'd', 'a', 't', 'a' };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    WavFileParser::WavFileParser()
+    {
+        ::memset(&m_header, 0, sizeof(m_header));
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    WavFileParser::~WavFileParser()
+    {
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    size_t WavFileParser::ParseHeader(AZ::IO::FileIOStream& fileStream)
+    {
+        if (IsHeaderValid())
+        {
+            // Header was already parsed then, no work needed.
+            return 0;
+        }
+
+        AZ_Assert(fileStream.IsOpen(), "WavFileParser::ParseHeader - FileIOStream is not open!\n");
+
+        // Parsers are allowed to seek into the stream if they want in order to perform their task
+        // of gathering file information.  Will return the byte-offset into the file where the
+        // data starts.
+        fileStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
+
+        // Begin parsing, start with the RIFF + WAVE tags...
+        AZ::u8* writePtr = reinterpret_cast<AZ::u8*>(&m_header);
+        size_t copySize = sizeof(m_header.riff) + sizeof(m_header.wave);
+        fileStream.Read(copySize, writePtr);
+
+        if (!ValidTag(m_header.riff.tag, WavFileParser::riff_tag))
+        {
+            AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Not a 'RIFF'!\n");
+            return 0;
+        }
+
+        if (!ValidTag(m_header.wave, WavFileParser::wave_tag))
+        {
+            AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Not a 'RIFF / WAVE'!\n");
+            return 0;
+        }
+
+        writePtr += copySize;
+
+        bool formatTagFound = false;
+        bool dataTagFound = false;
+
+        while (!dataTagFound)
+        {
+            // read the next tag, check what it is...
+            ChunkHeader header;
+            copySize = sizeof(header);
+            fileStream.Read(copySize, &header);
+
+            if (ValidTag(header.tag, WavFileParser::fmt__tag))
+            {
+                m_header.fmt.header = header;
+                writePtr = reinterpret_cast<AZ::u8*>(&m_header.fmt);
+                writePtr += sizeof(m_header.fmt.header);    // skip forward because it was already read into the temp chunkheader.
+                copySize = sizeof(m_header.fmt) - sizeof(m_header.fmt.header);
+                fileStream.Read(copySize, writePtr);
+
+                formatTagFound = true;
+            }
+            else if (ValidTag(header.tag, WavFileParser::data_tag))
+            {
+                m_header.data = header;
+
+                dataTagFound = true;
+            }
+            else
+            {
+                // Unknown tag, skip by the size specified
+                // It is possible that we want to read certain tag data in the future.
+                // Tools/encoders may embed extra data in various sections.
+                fileStream.Seek(header.size, AZ::IO::GenericStream::ST_SEEK_CUR);
+            }
+
+            // Check for Eof (premature)...
+            if (fileStream.GetCurPos() == fileStream.GetLength())
+            {
+                AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Got to end of file and did not locate a 'data' chunk!\n");
+                return 0;
+            }
+        }
+
+        if (!ValidTag(m_header.fmt.header.tag, WavFileParser::fmt__tag))
+        {
+            AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Did not find a 'fmt' tag!\n");
+        }
+
+        if (!ValidTag(m_header.data.tag, WavFileParser::data_tag))
+        {
+            AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Did not find a 'data' tag!\n");
+        }
+
+#ifdef AZ_DEBUG_BUILD
+        if (formatTagFound)
+        {
+            AZ_TracePrintf("WavFileParser", "Format: %u\n", static_cast<AZ::u32>(GetSampleType()));
+            AZ_TracePrintf("WavFileParser", "Channels: %u\n", GetNumChannels());
+            AZ_TracePrintf("WavFileParser", "SampleRate: %u\n", GetSampleRate());
+            AZ_TracePrintf("WavFileParser", "ByteRate: %u\n", GetByteRate());
+            AZ_TracePrintf("WavFileParser", "BitsPerSample: %u\n", GetBitsPerSample());
+            AZ_TracePrintf("WavFileParser", "DataSize: %u\n", GetDataSize());
+        }
+#endif // AZ_DEBUG_BUILD
+
+        if (dataTagFound && formatTagFound)
+        {
+            m_headerIsValid = true;
+            return fileStream.GetCurPos();
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioInputSampleType WavFileParser::GetSampleType() const
+    {
+        switch (m_header.fmt.audioFormat)
+        {
+        case 1:
+            return AudioInputSampleType::Int;
+        case 3:
+            return AudioInputSampleType::Float;
+        default:
+            return AudioInputSampleType::Unsupported;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // static
+    AZ_FORCE_INLINE bool WavFileParser::ValidTag(const AZ::u8 tag[4], const AZ::u8 name[4])
+    {
+        return (tag[0] == name[0] && tag[1] == name[1] && tag[2] == name[2] && tag[3] == name[3]);
+    }
+
+} // namespace Audio

+ 127 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/WavParser.h

@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AudioInput/AudioInputFile.h>
+
+namespace Audio
+{
+    /**
+     * A RIFF format chunk header.
+     */
+    struct ChunkHeader
+    {
+        AZ::u8 tag[4];
+        AZ::u32 size;
+    };
+
+    /**
+     * A WAVE format "fmt" chunk.
+     */
+    struct FmtChunk
+    {
+        ChunkHeader header;
+        AZ::u16 audioFormat;
+        AZ::u16 numChannels;
+        AZ::u32 sampleRate;
+        AZ::u32 byteRate;
+        AZ::u16 blockAlign;
+        AZ::u16 bitsPerSample;
+    };
+
+    /**
+     * A WAVE format header.
+     */
+    struct WavHeader
+    {
+        ChunkHeader riff;
+        AZ::u8 wave[4];
+        FmtChunk fmt;
+        ChunkHeader data;
+
+        static const size_t MinSize = 44;
+    };
+
+    static_assert(sizeof(WavHeader) == WavHeader::MinSize, "WavHeader struct size is not 44 bytes!");
+
+
+    /**
+     * Type of AudioFileParser for Wav File Format.
+     * Parses header information from Wav files and stores it for retrieval.
+     */
+    class WavFileParser
+        : public AudioFileParser
+    {
+    public:
+        AUDIO_IMPL_CLASS_ALLOCATOR(WavFileParser)
+
+        WavFileParser();
+        ~WavFileParser() override;
+
+        size_t ParseHeader(AZ::IO::FileIOStream& fileStream) override;
+
+        bool IsHeaderValid() const override;
+
+        AudioInputSampleType GetSampleType() const override;
+        AZ::u32 GetNumChannels() const override;
+        AZ::u32 GetSampleRate() const override;
+        AZ::u32 GetByteRate() const override;
+        AZ::u32 GetBitsPerSample() const override;
+        AZ::u32 GetDataSize() const override;
+
+    private:
+        static bool ValidTag(const AZ::u8 tag[4], const AZ::u8 name[4]);
+
+        WavHeader m_header;
+        bool m_headerIsValid = false;
+
+        static const AZ::u8 riff_tag[4];
+        static const AZ::u8 wave_tag[4];
+        static const AZ::u8 fmt__tag[4];
+        static const AZ::u8 data_tag[4];
+    };
+
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AZ_INLINE bool WavFileParser::IsHeaderValid() const
+    {
+        return m_headerIsValid;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AZ_INLINE AZ::u32 WavFileParser::GetNumChannels() const
+    {
+        return m_header.fmt.numChannels;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AZ_INLINE AZ::u32 WavFileParser::GetSampleRate() const
+    {
+        return m_header.fmt.sampleRate;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AZ_INLINE AZ::u32 WavFileParser::GetByteRate() const
+    {
+        return m_header.fmt.byteRate;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AZ_INLINE AZ::u32 WavFileParser::GetBitsPerSample() const
+    {
+        return m_header.fmt.bitsPerSample;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AZ_INLINE AZ::u32 WavFileParser::GetDataSize() const
+    {
+        return m_header.data.size;
+    }
+
+} // namespace Audio

+ 371 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioSourceManager.cpp

@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AudioSourceManager.h>
+#include <AudioInput/AudioInputFile.h>
+#include <AudioInput/AudioInputMicrophone.h>
+#include <AudioInput/AudioInputStream.h>
+
+#include <AzCore/std/parallel/lock.h>
+
+#include <AK/AkWwiseSDKVersion.h>
+#include <AK/Plugin/AkAudioInputPlugin.h>
+
+namespace Audio
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Audio Input Source
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    bool AudioInputSource::IsFormatValid() const
+    {
+        // Audio Input Source has restrictions on the formats that are supported:
+        // 16-bit Integer samples, interleaved samples
+        // 32-bit Float samples, non-interleaved samples
+        // The Parser doesn't care about such restrictions and is only responsible for
+        // reading the header information and validating it.
+
+        bool valid = true;
+
+        if (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample != 16)
+        {
+            valid = false;
+        }
+
+        if (m_config.m_sampleType == AudioInputSampleType::Float && m_config.m_bitsPerSample != 32)
+        {
+            valid = false;
+        }
+
+        if (m_config.m_sampleType == AudioInputSampleType::Unsupported)
+        {
+            valid = false;
+        }
+
+        if (!valid)
+        {
+            AZ_TracePrintf("AudioInputFile", "The file format is NOT supported!  Only 16-bit integer or 32-bit float sample types are allowed!\n"
+                "Current Format: (%s / %d)\n", m_config.m_sampleType == AudioInputSampleType::Int ? "Int"
+                : (m_config.m_sampleType == AudioInputSampleType::Float ? "Float" : "Unknown"),
+                m_config.m_bitsPerSample);
+        }
+
+        return valid;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputSource::SetFormat(AkAudioFormat& format)
+    {
+        AkUInt32 speakerConfig = 0;
+        switch (m_config.m_numChannels)
+        {
+            case 1:
+            {
+                speakerConfig = AK_SPEAKER_SETUP_MONO;
+                break;
+            }
+            case 2:
+            {
+                speakerConfig = AK_SPEAKER_SETUP_STEREO;
+                break;
+            }
+            case 6:
+            {
+                speakerConfig = AK_SPEAKER_SETUP_5POINT1;
+                break;
+            }
+            default:
+            {
+                // TODO: Test more channels
+                return;
+            }
+        }
+
+        AkUInt32 sampleType = 0;
+        AkUInt32 sampleInterleaveType = 0;
+        switch (m_config.m_bitsPerSample)
+        {
+            case 16:
+            {
+                sampleType = AK_INT;
+                sampleInterleaveType = AK_INTERLEAVED;
+                break;
+            }
+            case 32:
+            {
+                sampleType = AK_FLOAT;
+                sampleInterleaveType = AK_NONINTERLEAVED;
+                break;
+            }
+            default:
+            {
+                // Anything else and Audio Input Source doesn't support it.
+                // But we've already checked the format when parsing the header, so we shouldn't get here.
+                break;
+            }
+        }
+
+        AkChannelConfig akChannelConfig(m_config.m_numChannels, speakerConfig);
+
+        format.SetAll(
+            m_config.m_sampleRate,
+            akChannelConfig,
+            m_config.m_bitsPerSample,
+            m_config.m_numChannels * m_config.m_bitsPerSample >> 3, // shift converts bits->bytes, this is the frame size
+            sampleType,
+            sampleInterleaveType
+        );
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioInputSource::SetSourceId(TAudioSourceId sourceId)
+    {
+        m_config.m_sourceId = sourceId;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    TAudioSourceId AudioInputSource::GetSourceId() const
+    {
+        return m_config.m_sourceId;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Audio Input Source Manager
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioSourceManager::AudioSourceManager()
+    {
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AudioSourceManager::~AudioSourceManager()
+    {
+        Shutdown();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // static
+    AudioSourceManager& AudioSourceManager::Get()
+    {
+        static AudioSourceManager s_manager;
+        return s_manager;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // static
+    void AudioSourceManager::Initialize()
+    {
+        // Wwise Api call to setup the callbacks used by Audio Input Sources.
+        SetAudioInputCallbacks(AudioSourceManager::ExecuteCallback, AudioSourceManager::GetFormatCallback);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioSourceManager::Shutdown()
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
+
+        m_activeAudioInputs.clear();
+        m_inactiveAudioInputs.clear();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    bool AudioSourceManager::CreateSource(const SAudioInputConfig& sourceConfig)
+    {
+        AZStd::unique_ptr<AudioInputSource> ptr = nullptr;
+        switch (sourceConfig.m_sourceType)
+        {
+            case AudioInputSourceType::PcmFile:
+            case AudioInputSourceType::WavFile:
+            //case AudioInputSourceType::OggFile:
+            //case AudioInputSourceType::OpusFile:
+            {
+                if (!sourceConfig.m_sourceFilename.empty())
+                {
+                    ptr.reset(aznew AudioInputFile(sourceConfig));
+                }
+                break;
+            }
+            case AudioInputSourceType::Microphone:
+            {
+                ptr.reset(aznew AudioInputMicrophone(sourceConfig));
+                break;
+            }
+            case AudioInputSourceType::ExternalStream:
+            {
+                ptr.reset(aznew AudioInputStreaming(sourceConfig));
+                break;
+            }
+            case AudioInputSourceType::Synthesis:       // Will need to allow setting a user-defined Generate callback.
+            default:
+            {
+                AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::CreateSource - The type of AudioInputSource requested is not supported yet!\n");
+                return INVALID_AUDIO_SOURCE_ID;
+            }
+        }
+
+        if (!ptr || !ptr->IsOk())
+        {   // this check could change in the future as we add asynch loading.
+            return false;
+        }
+
+        AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
+
+        ptr->SetSourceId(sourceConfig.m_sourceId);
+        m_inactiveAudioInputs.emplace(sourceConfig.m_sourceId, AZStd::move(ptr));
+
+        return true;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioSourceManager::ActivateSource(TAudioSourceId sourceId, AkPlayingID playingId)
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
+
+        if (m_inactiveAudioInputs.find(sourceId) != m_inactiveAudioInputs.end())
+        {
+            if (m_activeAudioInputs.find(playingId) == m_activeAudioInputs.end())
+            {
+                m_inactiveAudioInputs[sourceId]->SetSourceId(sourceId);
+                m_activeAudioInputs[playingId] = AZStd::move(m_inactiveAudioInputs[sourceId]);
+                m_inactiveAudioInputs.erase(sourceId);
+
+                m_activeAudioInputs[playingId]->OnActivated();
+            }
+            else
+            {
+                AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::ActivateSource - Active source with playing Id %u already exists!\n", playingId);
+            }
+        }
+        else
+        {
+            AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::ActivateSource - Source with Id %u not found!\n", sourceId);
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioSourceManager::DeactivateSource(AkPlayingID playingId)
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
+
+        if (m_activeAudioInputs.find(playingId) != m_activeAudioInputs.end())
+        {
+            TAudioSourceId sourceId = m_activeAudioInputs[playingId]->GetSourceId();
+            if (m_inactiveAudioInputs.find(sourceId) == m_inactiveAudioInputs.end())
+            {
+                m_inactiveAudioInputs[sourceId] = AZStd::move(m_activeAudioInputs[playingId]);
+                m_activeAudioInputs.erase(playingId);
+
+                // Signal to the audio input source that it was deactivated!  It might unload it's resources.
+                m_inactiveAudioInputs[sourceId]->OnDeactivated();
+
+                if (!m_inactiveAudioInputs[sourceId]->IsOk())
+                {
+                    m_inactiveAudioInputs.erase(sourceId);
+                }
+            }
+            else
+            {
+                AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::DeactivateSource - Source with Id %u was already inactive!\n", sourceId);
+            }
+        }
+        else
+        {
+            AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::DeactivateSource - Active source with playing Id %u not found!\n", playingId);
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioSourceManager::DestroySource(TAudioSourceId sourceId)
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
+
+        if (m_inactiveAudioInputs.find(sourceId) != m_inactiveAudioInputs.end())
+        {
+            m_inactiveAudioInputs.erase(sourceId);
+        }
+        else
+        {
+            AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::DestroySource - No source with Id %u was found!\nDid you call DeactivateSource first on the playingId??\n", sourceId);
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    AkPlayingID AudioSourceManager::FindPlayingSource(TAudioSourceId sourceId)
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
+
+        for (auto& inputPair : m_activeAudioInputs)
+        {
+            if (inputPair.second->GetSourceId() == sourceId)
+            {
+                return inputPair.first;
+            }
+        }
+
+        return AK_INVALID_PLAYING_ID;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // static
+    void AudioSourceManager::ExecuteCallback(AkPlayingID playingId, AkAudioBuffer* akBuffer)
+    {
+        if (!akBuffer->HasData())
+        {
+            akBuffer->eState = AK_Fail;
+            akBuffer->uValidFrames = 0;
+            return;
+        }
+
+        if (akBuffer->eState == AK_NoDataNeeded)
+        {
+            akBuffer->eState = AK_NoDataReady;
+            akBuffer->uValidFrames = 0;
+            return;
+        }
+
+        AZStd::lock_guard<AZStd::mutex> lock(Get().m_inputMutex);
+
+        auto inputIter = Get().m_activeAudioInputs.find(playingId);
+        if (inputIter != Get().m_activeAudioInputs.end())
+        {
+            auto& audioInput = inputIter->second;
+            if (audioInput)
+            {
+                // this will set the uValidFrames and eState for us.
+                audioInput->WriteOutput(akBuffer);
+            }
+        }
+        else
+        {
+            // signal that the audio input playback should end.
+            akBuffer->eState = AK_NoMoreData;
+            akBuffer->uValidFrames = 0;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // static
+    void AudioSourceManager::GetFormatCallback(AkPlayingID playingId, AkAudioFormat& audioFormat)
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(Get().m_inputMutex);
+
+        auto inputIter = Get().m_activeAudioInputs.find(playingId);
+        if (inputIter != Get().m_activeAudioInputs.end())
+        {
+            // Set the AkAudioFormat from the AudioInputSource's SAudioInputConfig
+            auto& audioInput = inputIter->second;
+            if (audioInput)
+            {
+                audioInput->SetFormat(audioFormat);
+            }
+        }
+    }
+
+} // namespace Audio

+ 141 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioSourceManager.h

@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/PlatformIncl.h> // This include is needed to include WinSock2.h before including Windows.h
+                                 // As AK/SoundEngine/Common/AkTypes.h eventually includes Windows.h
+#include <IAudioInterfacesCommonData.h>
+
+#include <AzCore/std/containers/unordered_map.h>
+#include <AzCore/std/parallel/mutex.h>
+#include <AzCore/std/smart_ptr/unique_ptr.h>
+
+#include <AzCore/IO/FileIO.h>
+
+#include <AudioAllocators.h>
+
+#include <AK/SoundEngine/Common/AkTypes.h>
+#include <AK/SoundEngine/Common/IAkPlugin.h>
+
+namespace Audio
+{
+    /**
+     * Base class for Audio Input Source types.
+     * Represents an Audio Input Source, which has input/output routines and configuration information.
+     */
+    class AudioInputSource
+    {
+    public:
+        AUDIO_IMPL_CLASS_ALLOCATOR(AudioInputSource)
+
+        AudioInputSource() = default;
+        virtual ~AudioInputSource() = default;
+
+        virtual void ReadInput(const AudioStreamData& data) = 0;
+        virtual void WriteOutput(AkAudioBuffer* akBuffer) = 0;
+
+        virtual bool IsOk() const = 0;
+        virtual bool IsFormatValid() const;
+
+        virtual void OnActivated() {}
+        virtual void OnDeactivated() {}
+
+        void SetFormat(AkAudioFormat& format);
+        void SetSourceId(TAudioSourceId sourceId);
+        TAudioSourceId GetSourceId() const;
+
+    protected:
+        SAudioInputConfig m_config;     ///< Configuration information for the source type.
+        AkPlayingID m_playingId = AK_INVALID_PLAYING_ID;    ///< Playing ID of the source.
+    };
+
+
+    /**
+     * Manager class for AudioInputSource.
+     * Manages lifetime of AudioInputSource objects as they are created, activated, deactivated, and destroyed.
+     * The lifetime of an Audio Input Source:
+     * CreateSource (loads resources)
+     *  ActivateSource (once you obtain a playing Id)
+     *  (Running, callbacks being received, also async loading input if enabled)
+     *  DeactivateSource (once it's determined to be done playing)
+     * DestroySource (unloads resources)
+     */
+    class AudioSourceManager
+    {
+    public:
+        AudioSourceManager();
+        ~AudioSourceManager();
+
+        static AudioSourceManager& Get();
+        static void Initialize();
+        void Shutdown();
+
+        /**
+         * CreateSource a new AudioInputSource.
+         * Creates an AudioInputSource, based on the SAudioInputConfig and stores it in an inactive state.
+         * @param sourceConfig Configuration of the AudioInputSource.
+         * @return True if the source was created successfully, false otherwise.
+         */
+        bool CreateSource(const SAudioInputConfig& sourceConfig);
+
+        /**
+         * Activates an AudioInputSource.
+         * Moves a source from the inactive state to an active state by assigning an AkPlayingID.
+         * @param sourceId ID of the source (returned by CreateSource).
+         * @param playingId A playing ID of the source that is now playing in Wwise.
+         */
+        void ActivateSource(TAudioSourceId sourceId, AkPlayingID playingId);
+
+        /**
+         * Deactivates an AudioInputSource.
+         * Moves a source from the active state back to an inactive state, will happen when an end event callback is recieved.
+         * @param playingId Playing ID of the source that ended.
+         */
+        void DeactivateSource(AkPlayingID playingId);
+
+        /**
+         * Destroy an AudioInputSource.
+         * Destroys an AudioInputSource from the manager when it is no longer needed.
+         * @param sourceId Source ID of the object to remove.
+         */
+        void DestroySource(TAudioSourceId sourceId);
+
+        /**
+         * Find the Playing ID of a source.
+         * Given a Source ID, check if there are sources in the active state and if so, return their Playing ID.
+         * @param sourceId Source ID to look for in the active sources.
+         */
+        AkPlayingID FindPlayingSource(TAudioSourceId sourceId);
+
+    private:
+        /**
+         * Wwise Audio Input Plugin "Execute" callback function.
+         * This will be called whenever a playing Audio Input Source needs to be fed.
+         * @param playingId The Playing ID of the source.
+         * @param audioBuffer The buffer to copy samples into.
+         */
+        static void ExecuteCallback(AkPlayingID playingId, AkAudioBuffer* audioBuffer);
+
+        /**
+         * Wwise Audio Input Plugin "GetFormat" callback function.
+         * This will be called once whenever a new Audio Input Source is starting playback.
+         * @param playingId The Playing ID of the source.
+         * @param audioFormat The format structure that should be filled with format information.
+         */
+        static void GetFormatCallback(AkPlayingID playingId, AkAudioFormat& audioFormat);
+
+        AZStd::mutex m_inputMutex;      ///< Callbacks will come from the Wwise event processing thread.
+
+        template <typename KeyType, typename ValueType>
+        using AudioInputMap = AZStd::unordered_map<KeyType, AZStd::unique_ptr<ValueType>, AZStd::hash<KeyType>, AZStd::equal_to<KeyType>, Audio::AudioImplStdAllocator>;
+
+        AudioInputMap<TAudioSourceId, AudioInputSource> m_inactiveAudioInputs;      ///< Sources that haven't started playing yet.
+        AudioInputMap<AkPlayingID, AudioInputSource> m_activeAudioInputs;           ///< Sources that are currently playing.
+    };
+}

+ 56 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioSystemImplCVars.cpp

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#include <AudioSystemImplCVars.h>
+
+#include <AudioEngineWwise_Traits_Platform.h>
+
+namespace Audio::Wwise::Cvars
+{
+    AZ_CVAR(AZ::u64, s_PrimaryMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE,
+        nullptr, AZ::ConsoleFunctorFlags::Null,
+        "The size in KiB of the primary memory pool used by the Wwise audio integration.\n"
+        "Usage: s_PrimaryMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "\n");
+
+    AZ_CVAR(AZ::u64, s_SecondaryMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE,
+        nullptr, AZ::ConsoleFunctorFlags::Null,
+        "The size in KiB of the secondary memory pool.  Most platforms do not use this.\n"
+        "Usage: s_SecondaryMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "\n");
+
+    AZ_CVAR(AZ::u64, s_StreamDeviceMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE,
+        nullptr, AZ::ConsoleFunctorFlags::Null,
+        "The size in KiB of the Wwise Stream Device.\n"
+        "Usage: s_StreamDeviceMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE_DEFAULT_TEXT "\n");
+
+    AZ_CVAR(AZ::u64, s_CommandQueueMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE,
+        nullptr, AZ::ConsoleFunctorFlags::Null,
+        "The size in KiB of the Wwise Command Queue.\n"
+        "Usage: s_CommandQueueMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "\n");
+
+#if !defined(WWISE_RELEASE)
+    AZ_CVAR(AZ::u64, s_MonitorQueueMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE,
+        nullptr, AZ::ConsoleFunctorFlags::Null,
+        "The size in KiB of the Wwise Monitor Queue.\n"
+        "Not available in Release build.\n"
+        "Usage: s_MonitorQueueMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "\n");
+
+    AZ_CVAR(bool, s_EnableCommSystem, false,
+        nullptr, AZ::ConsoleFunctorFlags::Null,
+        "Enable initialization of the Wwise Comm system, which allows for remote profiling.\n"
+        "Not available in Release build.\n"
+        "Usage: s_EnableCommSystem=true (false)\n");
+
+    AZ_CVAR(bool, s_EnableOutputCapture, false,
+        nullptr, AZ::ConsoleFunctorFlags::Null,
+        "Capture the main audio output to a WAV file.\n"
+        "Not available in Release build.\n"
+        "Usage: s_EnableOutputCapture=true (false)\n");
+#endif // !WWISE_RELEASE
+
+} // namespace Audio::Wwise::Cvars

+ 27 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioSystemImplCVars.h

@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#pragma once
+
+#include <AzCore/Console/IConsole.h>
+
+namespace Audio::Wwise::Cvars
+{
+    AZ_CVAR_EXTERNED(AZ::u64, s_PrimaryMemorySize);
+    AZ_CVAR_EXTERNED(AZ::u64, s_SecondaryMemorySize);
+    AZ_CVAR_EXTERNED(AZ::u64, s_StreamDeviceMemorySize);
+    AZ_CVAR_EXTERNED(AZ::u64, s_CommandQueueMemorySize);
+
+#if !defined(WWISE_RELEASE)
+    AZ_CVAR_EXTERNED(AZ::u64, s_MonitorQueueMemorySize);
+    AZ_CVAR_EXTERNED(bool, s_EnableCommSystem);
+    AZ_CVAR_EXTERNED(bool, s_EnableOutputCapture);
+#endif // !WWISE_RELEASE
+
+} // namespace Audio::Wwise::Cvars

+ 2137 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioSystemImpl_wwise.cpp

@@ -0,0 +1,2137 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#include <AzCore/PlatformIncl.h>
+#include <AudioSystemImpl_wwise.h>
+
+#include <platform.h>
+#include <AzCore/AzCore_Traits_Platform.h>
+#include <AzCore/Console/ILogger.h>
+#include <AzCore/Debug/Profiler.h>
+#include <AzCore/std/containers/set.h>
+#include <AzCore/std/string/conversions.h>
+#include <AzCore/StringFunc/StringFunc.h>
+
+#include <IAudioSystem.h>
+#include <AudioAllocators.h>
+#include <AudioSourceManager.h>
+#include <AudioSystemImplCVars.h>
+#include <Common_wwise.h>
+#include <Config_wwise.h>
+#include <FileIOHandler_wwise.h>
+
+#include <AK/SoundEngine/Common/AkMemoryMgr.h>          // Memory Manager
+#include <AK/SoundEngine/Common/AkModule.h>             // Memory and Stream Manager interfaces
+#include <AK/SoundEngine/Common/AkSoundEngine.h>        // Sound Engine
+#include <AK/SpatialAudio/Common/AkSpatialAudio.h>      // Spatial Audio
+#include <AK/MusicEngine/Common/AkMusicEngine.h>        // Music Engine
+
+#include <PluginRegistration_wwise.h>                   // Registration of default set of plugins, customize this header to your needs.
+
+#include <inttypes.h>
+
+#if !defined(WWISE_RELEASE)
+    #include <AK/Comm/AkCommunication.h>    // Communication between Wwise and the game (excluded in release build)
+    #include <AK/Tools/Common/AkMonitorError.h>
+    #include <AkPlatformFuncs_Platform.h>
+#endif // WWISE_RELEASE
+
+#if defined(AK_MAX_AUX_PER_OBJ)
+    #define LY_MAX_AUX_PER_OBJ  AK_MAX_AUX_PER_OBJ
+#else
+    #define LY_MAX_AUX_PER_OBJ  (4)
+#endif
+
+
+namespace Audio
+{
+    namespace Platform
+    {
+        void InitializeMemory();
+        void SetupAkSoundEngine(AkPlatformInitSettings& platformInitSettings);
+    }
+
+    /////////////////////////////////////////////////////////////////////////////////
+    //                          AK MEMORY HOOKS SETUP
+    namespace Wwise::MemHooks
+    {
+        void* Malloc(AkMemPoolId memId, size_t size)
+        {
+            size_t memCategory = memId & AkMemID_MASK;
+            AZ_Assert(memCategory < AkMemID_NUM, "Wwise::MemHooks::Malloc - Bad AkMemPoolId passed: %zu", memCategory);
+            return AZ::AllocatorInstance<AudioImplAllocator>::Get().Allocate(size, 0, 0,
+                (memCategory < AkMemID_NUM) ? MemoryManagerCategories[memCategory] : nullptr);
+        }
+
+        void* Malign(AkMemPoolId memId, size_t size, AkUInt32 alignment)
+        {
+            size_t memCategory = memId & AkMemID_MASK;
+            AZ_Assert(memCategory < AkMemID_NUM, "WWise::MemHooks::Malign - Bad AkMemPoolId passed: %zu", memCategory);
+            return AZ::AllocatorInstance<AudioImplAllocator>::Get().Allocate(size, alignment, 0,
+                (memCategory < AkMemID_NUM) ? MemoryManagerCategories[memCategory] : nullptr);
+        }
+
+        void* Realloc([[maybe_unused]] AkMemPoolId memId, void* address, size_t size)
+        {
+            return AZ::AllocatorInstance<AudioImplAllocator>::Get().ReAllocate(address, size, 0);
+        }
+
+        void* ReallocAligned([[maybe_unused]] AkMemPoolId memId, void* address, size_t size, AkUInt32 alignment)
+        {
+            return AZ::AllocatorInstance<AudioImplAllocator>::Get().ReAllocate(address, size, alignment);
+        }
+
+        void Free([[maybe_unused]] AkMemPoolId memId, void* address)
+        {
+            AZ::AllocatorInstance<AudioImplAllocator>::Get().DeAllocate(address);
+        }
+
+        size_t TotalReservedMemorySize()
+        {
+            return AZ::AllocatorInstance<Audio::AudioImplAllocator>::Get().Capacity();
+        }
+
+        size_t SizeOfMemory([[maybe_unused]] AkMemPoolId memId, void* address)
+        {
+            return AZ::AllocatorInstance<Audio::AudioImplAllocator>::Get().AllocationSize(address);
+        }
+
+    } // namespace Wwise::MemHooks
+
+
+    const char* const CAudioSystemImpl_wwise::WwiseImplSubPath = "wwise/";
+    const char* const CAudioSystemImpl_wwise::WwiseGlobalAudioObjectName = "GlobalAudioObject";
+    const float CAudioSystemImpl_wwise::ObstructionOcclusionMin = 0.0f;
+    const float CAudioSystemImpl_wwise::ObstructionOcclusionMax = 1.0f;
+
+    static bool audioDeviceInitializationEvent = false;
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // AK callbacks
+    void WwiseEventCallback(AkCallbackType callbackType, AkCallbackInfo* callbackInfo)
+    {
+        if (callbackType == AK_EndOfEvent)
+        {
+            auto const eventData = static_cast<SATLEventData_wwise*>(callbackInfo->pCookie);
+
+            if (eventData)
+            {
+                Audio::CallbackRequest::ReportFinishedEvent reportFinishedEvent;
+                reportFinishedEvent.m_eventId = eventData->nATLID;
+                AZ::Interface<IAudioSystem>::Get()->PushRequest(AZStd::move(reportFinishedEvent));
+
+                if (eventData->nSourceId != INVALID_AUDIO_SOURCE_ID)
+                {
+                    AkPlayingID playingId = AudioSourceManager::Get().FindPlayingSource(eventData->nSourceId);
+                    AudioSourceManager::Get().DeactivateSource(playingId);
+                }
+            }
+        }
+        else if (callbackType == AK_Duration)
+        {
+            auto durationInfo = static_cast<AkDurationCallbackInfo*>(callbackInfo);
+            auto const eventData = static_cast<SATLEventData_wwise*>(callbackInfo->pCookie);
+            if (durationInfo && eventData && eventData->m_owner)
+            {
+                AudioTriggerNotificationBus::QueueEvent(
+                    TriggerNotificationIdType{ eventData->m_owner }, &AudioTriggerNotificationBus::Events::ReportDurationInfo,
+                    eventData->m_triggerId,
+                    eventData->nATLID,
+                    durationInfo->fDuration,
+                    durationInfo->fEstimatedDuration);
+            }
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void AudioDeviceCallback(
+        [[maybe_unused]] AK::IAkGlobalPluginContext* context,
+        [[maybe_unused]] AkUniqueID audioDeviceSharesetId,
+        [[maybe_unused]] AkUInt32 deviceId,
+        AK::AkAudioDeviceEvent deviceEvent,
+        [[maybe_unused]] AKRESULT inAkResult
+    )
+    {
+        if (deviceEvent == AK::AkAudioDeviceEvent_Initialization)
+        {
+            audioDeviceInitializationEvent = true;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void PrepareEventCallback(
+        AkUniqueID akEventId,
+        [[maybe_unused]] const void* bankPtr,
+        [[maybe_unused]] AKRESULT loadResult,
+        [[maybe_unused]] AkMemPoolId memPoolId,
+        void* cookie)
+    {
+        auto const eventData = static_cast<SATLEventData_wwise*>(cookie);
+
+        if (eventData)
+        {
+            eventData->nAKID = akEventId;
+
+            // TODO (PrepareTrigger/PrepareEvent functionality):
+            // Audio::CallbackRequest::ReportFinishedEvent (eventData->nATLID, loadResult == AK_Success)
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+#if !defined(WWISE_RELEASE)
+    static void ErrorMonitorCallback(
+        AK::Monitor::ErrorCode in_eErrorCode,       ///< Error code number value
+        const AkOSChar* in_pszError,                ///< Message or error string to be displayed
+        AK::Monitor::ErrorLevel /*in_eErrorLevel*/, ///< Specifies whether it should be displayed as a message or an error
+        AkPlayingID in_playingID,                   ///< Related Playing ID if applicable, AK_INVALID_PLAYING_ID otherwise
+        AkGameObjectID in_gameObjID)                ///< Related Game Object ID if applicable, AK_INVALID_GAME_OBJECT otherwise
+    {
+        char* errorStr = nullptr;
+        CONVERT_OSCHAR_TO_CHAR(in_pszError, errorStr);
+        AZLOG_NOTICE(
+            "<Wwise> %s ErrorCode: %d PlayingID: %u GameObjID: %llu", errorStr, in_eErrorCode, in_playingID,
+            static_cast<AZ::u64>(in_gameObjID));
+    }
+#endif // WWISE_RELEASE
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    static int GetAssetType(const SATLSourceData* sourceData)
+    {
+        if (!sourceData)
+        {
+            return eAAT_NONE;
+        }
+
+        return sourceData->m_sourceInfo.m_codecType == eACT_STREAM_PCM
+               ? eAAT_STREAM
+               : eAAT_SOURCE;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    static int GetAkCodecID(EAudioCodecType codecType)
+    {
+        switch (codecType)
+        {
+// Wwise version 2022.x does not include AKCODECID_AAC
+#if defined(AKCODECID_AAC)
+        case eACT_AAC:
+            return AKCODECID_AAC;
+#endif // defined(AKCODECID_AAC)
+        case eACT_ADPCM:
+            return AKCODECID_ADPCM;
+        case eACT_PCM:
+            return AKCODECID_PCM;
+        case eACT_VORBIS:
+            return AKCODECID_VORBIS;
+        case eACT_XMA:
+            return AKCODECID_XMA;
+        case eACT_XWMA:
+            return AKCODECID_XWMA;
+        case eACT_STREAM_PCM:
+        default:
+            AZ_Assert(codecType, "Codec not supported");
+            return AKCODECID_VORBIS;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    CAudioSystemImpl_wwise::CAudioSystemImpl_wwise(const char* assetsPlatformName)
+        : m_globalGameObjectID(static_cast<AkGameObjectID>(GLOBAL_AUDIO_OBJECT_ID))
+        , m_defaultListenerGameObjectID(AK_INVALID_GAME_OBJECT)
+        , m_initBankID(AK_INVALID_BANK_ID)
+#if !defined(WWISE_RELEASE)
+        , m_isCommSystemInitialized(false)
+#endif // !WWISE_RELEASE
+    {
+        if (assetsPlatformName && assetsPlatformName[0] != '\0')
+        {
+            m_assetsPlatform = assetsPlatformName;
+        }
+
+        Platform::InitializeMemory();
+
+        SetBankPaths();
+
+#if !defined(WWISE_RELEASE)
+        m_fullImplString = AZStd::string::format("%s (%s)", WWISE_IMPL_VERSION_STRING, m_soundbankFolder.c_str());
+
+        // Set up memory categories for debug tracking, do this early before initializing Wwise so they are available
+        // before the any allocations through hooks occur.
+        AZLOG_INFO("%s", "Memory Categories:");
+        m_debugMemoryInfo.reserve(AkMemID_NUM + 1);
+
+        for (AZ::u32 memId = 0; memId < AkMemID_NUM; ++memId)
+        {
+            AudioImplMemoryPoolInfo memInfo;
+            azstrcpy(memInfo.m_poolName, sizeof(memInfo.m_poolName), Wwise::MemoryManagerCategories[memId]);
+            memInfo.m_poolId = memId;
+
+            m_debugMemoryInfo.push_back(memInfo);
+            AZLOG_INFO("Memory category ID: %u - '%s'", memId, Wwise::MemoryManagerCategories[memId]);
+        }
+
+        // Add one more category for global stats.
+        AudioImplMemoryPoolInfo memInfo;
+        azstrcpy(memInfo.m_poolName, sizeof(memInfo.m_poolName), "Global");
+        m_debugMemoryInfo.push_back(memInfo);
+#endif // !WWISE_RELEASE
+
+        AudioSystemImplementationRequestBus::Handler::BusConnect();
+        AudioSystemImplementationNotificationBus::Handler::BusConnect();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    CAudioSystemImpl_wwise::~CAudioSystemImpl_wwise()
+    {
+        AudioSystemImplementationRequestBus::Handler::BusDisconnect();
+        AudioSystemImplementationNotificationBus::Handler::BusDisconnect();
+    }
+
+
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // AudioSystemImplementationNotificationBus
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::OnAudioSystemLoseFocus()
+    {
+    #if AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND
+        AKRESULT akResult = AK::SoundEngine::Suspend();
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("Wwise failed to Suspend, AKRESULT %d", akResult);
+        }
+    #endif // AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::OnAudioSystemGetFocus()
+    {
+    #if AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND
+        AKRESULT akResult = AK::SoundEngine::WakeupFromSuspend();
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("Wwise failed to WakeupFromSuspend, AKRESULT %d", akResult);
+        }
+    #endif // AZ_TRAIT_AUDIOENGINEWWISE_AUDIOSYSTEMIMPL_USE_SUSPEND
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::OnAudioSystemMuteAll()
+    {
+        // With Wwise we drive this via events.
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::OnAudioSystemUnmuteAll()
+    {
+        // With Wwise we drive this via events.
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::OnAudioSystemRefresh()
+    {
+        AKRESULT akResult = AK_Fail;
+
+        if (m_initBankID != AK_INVALID_BANK_ID)
+        {
+            akResult = AK::SoundEngine::UnloadBank(m_initBankID, nullptr);
+
+            if (!IS_WWISE_OK(akResult))
+            {
+                AZLOG_ERROR("Wwise failed to unload %s, returned AKRESULT %d", Wwise::InitBank, akResult);
+            }
+        }
+
+        AkOSChar* initBankName = nullptr;
+        CONVERT_CHAR_TO_OSCHAR(Wwise::InitBank, initBankName);
+        akResult = AK::SoundEngine::LoadBank(initBankName, m_initBankID);
+
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("Wwise failed to load %s, returned AKRESULT %d", Wwise::InitBank, akResult);
+            m_initBankID = AK_INVALID_BANK_ID;
+        }
+    }
+
+
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // AudioSystemImplementationRequestBus
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::Update([[maybe_unused]] const float updateIntervalMS)
+    {
+        AZ_PROFILE_FUNCTION(Audio);
+
+        if (AK::SoundEngine::IsInitialized())
+        {
+    #if !defined(WWISE_RELEASE)
+            AKRESULT akResult = AK_Fail;
+            static bool enableOutputCapture = false;
+
+            if (Wwise::Cvars::s_EnableOutputCapture && !enableOutputCapture)
+            {
+                // This file ends up in the cache folder
+                // Need to disable this on LTX, it produces garbage output.  But there's no way to "IsLTX()" yet.
+                akResult = AK::SoundEngine::StartOutputCapture(AKTEXT("../wwise_audio_capture.wav"));
+                AZ_Assert(IS_WWISE_OK(akResult), "AK::SoundEngine::StartOutputCapture failed!");
+                enableOutputCapture = Wwise::Cvars::s_EnableOutputCapture;
+            }
+            else if (!Wwise::Cvars::s_EnableOutputCapture && enableOutputCapture)
+            {
+                akResult = AK::SoundEngine::StopOutputCapture();
+                AZ_Assert(IS_WWISE_OK(akResult), "AK::SoundEngine::StopOutputCapture failed!");
+                enableOutputCapture = Wwise::Cvars::s_EnableOutputCapture;
+            }
+
+            if (audioDeviceInitializationEvent)
+            {
+                AkChannelConfig channelConfig = AK::SoundEngine::GetSpeakerConfiguration();
+                int surroundSpeakers = channelConfig.uNumChannels;
+                int lfeSpeakers = 0;
+                if (AK::HasLFE(channelConfig.uChannelMask))
+                {
+                    --surroundSpeakers;
+                    ++lfeSpeakers;
+                }
+                m_speakerConfigString = AZStd::string::format("Output: %d.%d", surroundSpeakers, lfeSpeakers);
+                m_fullImplString = AZStd::string::format("%s (%s)  %s", WWISE_IMPL_VERSION_STRING, m_soundbankFolder.c_str(), m_speakerConfigString.c_str());
+
+                audioDeviceInitializationEvent = false;
+            }
+    #endif // !WWISE_RELEASE
+
+            AK::SoundEngine::RenderAudio();
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::Initialize()
+    {
+        // If something fails so severely during initialization that we need to fall back to a 'Null' implementation
+        // we will need to shut down what has been initialized so far. Therefore make sure to call Shutdown() before returning EAudioRequestStatus::Failure!
+
+        AkMemSettings akMemSettings;
+        AK::MemoryMgr::GetDefaultSettings(akMemSettings);
+        akMemSettings.pfMalloc = Wwise::MemHooks::Malloc;
+        akMemSettings.pfMalign = Wwise::MemHooks::Malign;
+        akMemSettings.pfRealloc = Wwise::MemHooks::Realloc;
+        akMemSettings.pfReallocAligned = Wwise::MemHooks::ReallocAligned;
+        akMemSettings.pfFree = Wwise::MemHooks::Free;
+        akMemSettings.pfTotalReservedMemorySize = Wwise::MemHooks::TotalReservedMemorySize;
+        akMemSettings.pfSizeOfMemory = Wwise::MemHooks::SizeOfMemory;
+        akMemSettings.uMemAllocationSizeLimit = Wwise::Cvars::s_PrimaryMemorySize << 10;
+
+        AKRESULT akResult = AK::MemoryMgr::Init(&akMemSettings);
+
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("AK::MemoryMgr::Init() returned AKRESULT %d", akResult);
+            ShutDown();
+            return EAudioRequestStatus::Failure;
+        }
+
+        akResult = AK::SoundEngine::RegisterAudioDeviceStatusCallback(AudioDeviceCallback);
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("AK::SoundEngine::RegisterAudioDeviceStatusCallback returned AKRESULT %d", akResult);
+        }
+
+        AkStreamMgrSettings akStreamSettings;
+        AK::StreamMgr::GetDefaultSettings(akStreamSettings);
+
+        if (AK::StreamMgr::Create(akStreamSettings) == nullptr)
+        {
+            AZLOG_ERROR("%s", "AK::StreamMrg::Create() failed!");
+            ShutDown();
+            return EAudioRequestStatus::Failure;
+        }
+
+        akResult = m_fileIOHandler.Init(Wwise::Cvars::s_StreamDeviceMemorySize << 10);
+
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("m_fileIOHandler.Init() returned AKRESULT %d", akResult);
+            ShutDown();
+            return EAudioRequestStatus::Failure;
+        }
+
+        m_fileIOHandler.SetBankPath(m_soundbankFolder.c_str());
+
+        AkInitSettings akInitSettings;
+        AK::SoundEngine::GetDefaultInitSettings(akInitSettings);
+        akInitSettings.uCommandQueueSize = aznumeric_cast<AkUInt32>(Wwise::Cvars::s_CommandQueueMemorySize << 10);
+#if !defined(WWISE_RELEASE)
+        akInitSettings.uMonitorQueuePoolSize = aznumeric_cast<AkUInt32>(Wwise::Cvars::s_MonitorQueueMemorySize << 10);
+#endif // !WWISE_RELEASE
+        akInitSettings.bEnableGameSyncPreparation = false;
+
+#if AZ_TRAIT_AUDIOENGINEWWISE_DEFAULT_SPEAKER_CONFIGURATION
+        akInitSettings.settingsMainOutput.channelConfig.SetStandardOrAnonymous(
+            AK::ChannelMaskToNumChannels(AZ_TRAIT_AUDIOENGINEWWISE_DEFAULT_SPEAKER_CONFIGURATION),
+            AZ_TRAIT_AUDIOENGINEWWISE_DEFAULT_SPEAKER_CONFIGURATION);
+#endif
+
+        AkPlatformInitSettings akPlatformInitSettings;
+        AK::SoundEngine::GetDefaultPlatformInitSettings(akPlatformInitSettings);
+
+        Platform::SetupAkSoundEngine(akPlatformInitSettings);
+
+        akResult = AK::SoundEngine::Init(&akInitSettings, &akPlatformInitSettings);
+
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("AK::SoundEngine::Init() returned AKRESULT %d", akResult);
+            ShutDown();
+            return EAudioRequestStatus::Failure;
+        }
+
+        AkMusicSettings akMusicSettings;
+        AK::MusicEngine::GetDefaultInitSettings(akMusicSettings);
+
+        akResult = AK::MusicEngine::Init(&akMusicSettings);
+
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("AK::MusicEngine::Init() returned AKRESULT %d", akResult);
+            ShutDown();
+            return EAudioRequestStatus::Failure;
+        }
+
+        AkSpatialAudioInitSettings akSpatialAudioSettings;
+        akResult = AK::SpatialAudio::Init(akSpatialAudioSettings);
+
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("AK::SpatialAudio::Init() returned AKRESULT %d", akResult);
+            ShutDown();
+            return EAudioRequestStatus::Failure;
+        }
+
+#if !defined(WWISE_RELEASE)
+        if (Audio::Wwise::Cvars::s_EnableCommSystem)
+        {
+            m_isCommSystemInitialized = true;
+            AkCommSettings akCommSettings;
+            AK::Comm::GetDefaultInitSettings(akCommSettings);
+
+            akResult = AK::Comm::Init(akCommSettings);
+
+            if (!IS_WWISE_OK(akResult))
+            {
+                AZLOG_ERROR(
+                    "AK::Comm::Init() returned AKRESULT %d.  Communication between the Wwise authoring application and the game will not "
+                    "be possible",
+                    akResult);
+                m_isCommSystemInitialized = false;
+            }
+
+            akResult = AK::Monitor::SetLocalOutput(AK::Monitor::ErrorLevel_All, ErrorMonitorCallback);
+
+            if (!IS_WWISE_OK(akResult))
+            {
+                AK::Comm::Term();
+                AZLOG_ERROR("AK::Monitor::SetLocalOutput() returned AKRESULT %d", akResult);
+                m_isCommSystemInitialized = false;
+            }
+        }
+#endif // !WWISE_RELEASE
+
+        // Initialize the AudioSourceManager
+        AudioSourceManager::Get().Initialize();
+
+        // Register the Global Audio Object used for the events that don't need a location in the game world
+        akResult = AK::SoundEngine::RegisterGameObj(m_globalGameObjectID, WwiseGlobalAudioObjectName);
+
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_ERROR("AK::SoundEngine::RegisterGameObject() failed for '%s' with AKRESULT %d", WwiseGlobalAudioObjectName, akResult);
+        }
+
+        // Load init.bnk before making the system available to the users
+        AkOSChar* initBankName = nullptr;
+        CONVERT_CHAR_TO_OSCHAR(Wwise::InitBank, initBankName);
+
+        akResult = AK::SoundEngine::LoadBank(initBankName, m_initBankID);
+        if (!IS_WWISE_OK(akResult))
+        {
+            // This does not qualify for a fallback to the 'Null' audio implementation!
+            AZLOG_ERROR("Wwise failed to load %s, returned AKRESULT %d", Wwise::InitBank, akResult);
+            m_initBankID = AK_INVALID_BANK_ID;
+        }
+
+        return EAudioRequestStatus::Success;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::ShutDown()
+    {
+        AKRESULT akResult = AK_Fail;
+
+#if !defined(WWISE_RELEASE)
+        if (m_isCommSystemInitialized)
+        {
+            AK::Comm::Term();
+
+            akResult = AK::Monitor::SetLocalOutput(0, nullptr);
+
+            if (!IS_WWISE_OK(akResult))
+            {
+                AZLOG_WARN("AK::Monitor::SetLocalOutput() returned AKRESULT %d", akResult);
+            }
+
+            m_isCommSystemInitialized = false;
+        }
+#endif // !WWISE_RELEASE
+
+        akResult = AK::SoundEngine::UnregisterAudioDeviceStatusCallback();
+        if (akResult != AK_Success)
+        {
+            AZLOG_WARN("AK::SoundEngine::UnregisterAudioDeviceStatusCallback() returned AKRESULT %d", akResult);
+        }
+
+        // Shutdown the AudioSourceManager
+        AudioSourceManager::Get().Shutdown();
+
+        AK::MusicEngine::Term();
+
+        if (AK::SoundEngine::IsInitialized())
+        {
+            // UnRegister the DummyGameObject
+            akResult = AK::SoundEngine::UnregisterGameObj(m_globalGameObjectID);
+
+            if (!IS_WWISE_OK(akResult))
+            {
+                AZLOG_WARN(
+                    "AK::SoundEngine::UnregisterGameObject() failed for '%s' with AKRESULT %d", WwiseGlobalAudioObjectName, akResult);
+            }
+
+            akResult = AK::SoundEngine::ClearBanks();
+
+            if (!IS_WWISE_OK(akResult))
+            {
+                AZLOG_ERROR("AK::SoundEngine::ClearBanks() returned AKRESULT %d", akResult);
+            }
+
+            AK::SoundEngine::Term();
+        }
+
+        // Terminate the streaming device and streaming manager
+        // CAkFilePackageLowLevelIOBlocking::Term() destroys its associated streaming device
+        // that lives in the Stream Manager, and unregisters itself as the File Location Resolver.
+        if (AK::IAkStreamMgr::Get())
+        {
+            m_fileIOHandler.ShutDown();
+            AK::IAkStreamMgr::Get()->Destroy();
+        }
+
+        // Terminate the Memory Manager
+        if (AK::MemoryMgr::IsInitialized())
+        {
+            AK::MemoryMgr::Term();
+        }
+
+        return EAudioRequestStatus::Success;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::Release()
+    {
+        // Deleting this object and destroying the allocator has been moved to AudioEngineWwiseSystemComponent
+        return EAudioRequestStatus::Success;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::StopAllSounds()
+    {
+        AK::SoundEngine::StopAll();
+        return EAudioRequestStatus::Success;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::RegisterAudioObject(
+        IATLAudioObjectData* const audioObjectData,
+        const char* const objectName)
+    {
+        if (audioObjectData)
+        {
+            auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+            const AKRESULT akResult = AK::SoundEngine::RegisterGameObj(implObjectData->nAKID, objectName);
+
+            const bool akSuccess = IS_WWISE_OK(akResult);
+
+            if (!akSuccess)
+            {
+                AZLOG_WARN("AK::SoundEngine::RegisterGameObj() returned AKRESULT %d", akResult);
+            }
+
+            return BoolToARS(akSuccess);
+        }
+        else
+        {
+            AZLOG_WARN("%s", "RegisterAudioObject failed, audioObjectData was null");
+            return EAudioRequestStatus::Failure;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::UnregisterAudioObject(IATLAudioObjectData* const audioObjectData)
+    {
+        if (audioObjectData)
+        {
+            auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+            const AKRESULT akResult = AK::SoundEngine::UnregisterGameObj(implObjectData->nAKID);
+
+            const bool akSuccess = IS_WWISE_OK(akResult);
+
+            if (!akSuccess)
+            {
+                AZLOG_WARN("AK::SoundEngine::UnregisterGameObj() returned AKRESULT %d", akResult);
+            }
+
+            return BoolToARS(akSuccess);
+        }
+        else
+        {
+            AZLOG_WARN("%s", "UnregisterAudioObject failed, audioObjectData was null");
+            return EAudioRequestStatus::Failure;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::ResetAudioObject(IATLAudioObjectData* const audioObjectData)
+    {
+        if (audioObjectData)
+        {
+            auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+            implObjectData->cEnvironmentImplAmounts.clear();
+            implObjectData->bNeedsToUpdateEnvironments = false;
+
+            return EAudioRequestStatus::Success;
+        }
+        else
+        {
+            AZLOG_WARN("%s", "ResetAudioObject failed, audioObjectData was null");
+            return EAudioRequestStatus::Failure;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::UpdateAudioObject(IATLAudioObjectData* const audioObjectData)
+    {
+        AZ_PROFILE_FUNCTION(Audio);
+
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        if (audioObjectData)
+        {
+            auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+            if (implObjectData->bNeedsToUpdateEnvironments)
+            {
+                result = PostEnvironmentAmounts(implObjectData);
+            }
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::PrepareTriggerSync(
+        [[maybe_unused]] IATLAudioObjectData* const audioObjectData,
+        const IATLTriggerImplData* const triggerData)
+    {
+        return PrepUnprepTriggerSync(triggerData, true);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::UnprepareTriggerSync(
+        [[maybe_unused]] IATLAudioObjectData* const audioObjectData,
+        const IATLTriggerImplData* const triggerData)
+    {
+        return PrepUnprepTriggerSync(triggerData, false);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::PrepareTriggerAsync(
+        [[maybe_unused]] IATLAudioObjectData* const audioObjectData,
+        const IATLTriggerImplData* const triggerData,
+        IATLEventData* const eventData)
+    {
+        return PrepUnprepTriggerAsync(triggerData, eventData, true);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::UnprepareTriggerAsync(
+        [[maybe_unused]] IATLAudioObjectData* const audioObjectData,
+        const IATLTriggerImplData* const triggerData,
+        IATLEventData* const eventData)
+    {
+        return PrepUnprepTriggerAsync(triggerData, eventData, false);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::ActivateTrigger(
+        IATLAudioObjectData* const audioObjectData,
+        const IATLTriggerImplData* const triggerData,
+        IATLEventData* const eventData,
+        const SATLSourceData* const sourceData)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+        auto const implTriggerData = static_cast<const SATLTriggerImplData_wwise*>(triggerData);
+        auto const implEventData = static_cast<SATLEventData_wwise*>(eventData);
+
+        if (implObjectData && implTriggerData && implEventData)
+        {
+            AkGameObjectID akObjectId = (implObjectData->bHasPosition ? implObjectData->nAKID : m_globalGameObjectID);
+
+            if (implObjectData->bHasPosition)
+            {
+                PostEnvironmentAmounts(implObjectData);
+            }
+
+            AkPlayingID akPlayingId = AK_INVALID_PLAYING_ID;
+            switch (GetAssetType(sourceData))
+            {
+                case eAAT_SOURCE:
+                {
+                    AZ_Assert(sourceData, "SourceData not provided for source type!");
+                    // format: "external/{collection_id}/{language_id}/{file_id}.wem"
+                    auto filePath = AZStd::string::format("%s/%llu/%llu/%llu.wem",
+                        Audio::Wwise::ExternalSourcesPath,
+                        sourceData->m_sourceInfo.m_collectionId,
+                        sourceData->m_sourceInfo.m_languageId,
+                        sourceData->m_sourceInfo.m_fileId);
+
+                    AkOSChar* finalFilePath = nullptr;
+                    CONVERT_CHAR_TO_OSCHAR(filePath.c_str(), finalFilePath);
+
+                    AkExternalSourceInfo sources[1];
+                    sources[0].iExternalSrcCookie = static_cast<AkUInt32>(sourceData->m_sourceInfo.m_sourceId);
+                    sources[0].szFile = finalFilePath;
+                    sources[0].idCodec = GetAkCodecID(sourceData->m_sourceInfo.m_codecType);
+
+                    akPlayingId = AK::SoundEngine::PostEvent(
+                        implTriggerData->nAKID,
+                        akObjectId,
+                        AK_EndOfEvent,
+                        &WwiseEventCallback,
+                        implEventData,
+                        1,
+                        sources);
+
+                    if (akPlayingId != AK_INVALID_PLAYING_ID)
+                    {
+                        implEventData->audioEventState = eAES_PLAYING;
+                        implEventData->nAKID = akPlayingId;
+                        result = EAudioRequestStatus::Success;
+                    }
+                    else
+                    {
+                        // if Posting an Event failed, try to prepare it, if it isn't prepared already
+                        AZLOG_WARN("Failed to post Wwise event %u with external source '%s'", implTriggerData->nAKID, filePath.c_str());
+                    }
+                    break;
+                }
+
+                case eAAT_STREAM:
+                    [[fallthrough]];
+                case eAAT_NONE:
+                    [[fallthrough]];
+                default:
+                {
+                    akPlayingId = AK::SoundEngine::PostEvent(
+                        implTriggerData->nAKID,
+                        akObjectId,
+                        AK_EndOfEvent | AK_Duration,
+                        &WwiseEventCallback,
+                        implEventData);
+
+                    if (akPlayingId != AK_INVALID_PLAYING_ID)
+                    {
+                        if (sourceData)
+                        {
+                            TAudioSourceId sourceId = sourceData->m_sourceInfo.m_sourceId;
+                            if (sourceId != INVALID_AUDIO_SOURCE_ID)
+                            {
+                                // Activate the audio input source (associates sourceId w/ playingId)...
+                                AudioSourceManager::Get().ActivateSource(sourceId, akPlayingId);
+                                implEventData->nSourceId = sourceId;
+                            }
+                        }
+
+                        implEventData->audioEventState = eAES_PLAYING;
+                        implEventData->nAKID = akPlayingId;
+                        result = EAudioRequestStatus::Success;
+                    }
+                    else
+                    {
+                        // if Posting an Event failed, try to prepare it, if it isn't prepared already
+                        AZLOG_WARN("Failed to post Wwise event %u", implTriggerData->nAKID);
+                    }
+                    break;
+                }
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData, ATLTriggerData, or EventData passed to ActivateTrigger");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::StopEvent(
+        [[maybe_unused]] IATLAudioObjectData* const audioObjectData,
+        const IATLEventData* const eventData)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implEventData = static_cast<const SATLEventData_wwise*>(eventData);
+
+        if (implEventData)
+        {
+            switch (implEventData->audioEventState)
+            {
+                case eAES_PLAYING:
+                {
+                    AK::SoundEngine::StopPlayingID(implEventData->nAKID, 10);
+                    result = EAudioRequestStatus::Success;
+                    break;
+                }
+                default:
+                {
+                    AZLOG_ERROR("%s", "Stopping an event in this state is not supported yet");
+                    break;
+                }
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid EventData passed to StopEvent");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::StopAllEvents(IATLAudioObjectData* const audioObjectData)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+        if (implObjectData)
+        {
+            const AkGameObjectID akObjectId = implObjectData->bHasPosition ? implObjectData->nAKID : m_globalGameObjectID;
+
+            AK::SoundEngine::StopAll(akObjectId);
+
+            result = EAudioRequestStatus::Success;
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData passed to StopAllEvents");
+        }
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::SetPosition(
+        IATLAudioObjectData* const audioObjectData,
+        const SATLWorldPosition& worldPosition)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+        if (implObjectData)
+        {
+            AkSoundPosition akSoundPos;
+            ATLTransformToAkTransform(worldPosition, akSoundPos);
+
+            const AKRESULT akResult = AK::SoundEngine::SetPosition(implObjectData->nAKID, akSoundPos);
+            if (IS_WWISE_OK(akResult))
+            {
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::SetPosition() returned AKRESULT %d", akResult);
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData passed to SetPosition");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::SetMultiplePositions(
+        IATLAudioObjectData* const audioObjectData,
+        const MultiPositionParams& multiPositionParams)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+        if (implObjectData)
+        {
+            AZStd::vector<AkSoundPosition> akPositions;
+            AZStd::for_each(multiPositionParams.m_positions.begin(), multiPositionParams.m_positions.end(),
+                [&akPositions](const auto& position)
+                {
+                    akPositions.emplace_back(AZVec3ToAkTransform(position));
+                }
+            );
+
+            AK::SoundEngine::MultiPositionType type = AK::SoundEngine::MultiPositionType_MultiDirections; // default 'Blended'
+
+            if (multiPositionParams.m_type == MultiPositionBehaviorType::Separate)
+            {
+                type = AK::SoundEngine::MultiPositionType_MultiSources;
+            }
+
+            const AKRESULT akResult = AK::SoundEngine::SetMultiplePositions(implObjectData->nAKID, akPositions.data(), static_cast<AkUInt16>(akPositions.size()), type);
+            if (IS_WWISE_OK(akResult))
+            {
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::SetMultiplePositions returned AKRESULT %d", akResult);
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData passed to SetMultiplePositions");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::SetEnvironment(
+        IATLAudioObjectData* const audioObjectData,
+        const IATLEnvironmentImplData* const environmentData,
+        const float amount)
+    {
+        static const float s_envEpsilon = 0.0001f;
+
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+        auto const implEnvironmentData = static_cast<const SATLEnvironmentImplData_wwise*>(environmentData);
+
+        if (implObjectData && implEnvironmentData)
+        {
+            switch (implEnvironmentData->eType)
+            {
+                case eWAET_AUX_BUS:
+                {
+                    float currentAmount = -1.f;
+                    auto it = implObjectData->cEnvironmentImplAmounts.find(implEnvironmentData->nAKBusID);
+                    if (it != implObjectData->cEnvironmentImplAmounts.end())
+                    {
+                        currentAmount = it->second;
+                    }
+
+                    if (currentAmount == -1.f || !AZ::IsClose(currentAmount, amount, s_envEpsilon))
+                    {
+                        implObjectData->cEnvironmentImplAmounts[implEnvironmentData->nAKBusID] = amount;
+                        implObjectData->bNeedsToUpdateEnvironments = true;
+                    }
+
+                    result = EAudioRequestStatus::Success;
+                    break;
+                }
+                case eWAET_RTPC:
+                {
+                    auto akRtpcValue = static_cast<AkRtpcValue>(implEnvironmentData->fMult * amount + implEnvironmentData->fShift);
+
+                    const AKRESULT akResult = AK::SoundEngine::SetRTPCValue(implEnvironmentData->nAKRtpcID, akRtpcValue, implObjectData->nAKID);
+
+                    if (IS_WWISE_OK(akResult))
+                    {
+                        result = EAudioRequestStatus::Success;
+                    }
+                    else
+                    {
+                        AZLOG_WARN("AK::SoundEngine::SetRTPCValue returned AKRESULT %d", akResult);
+                    }
+                    break;
+                }
+                default:
+                {
+                    AZ_Assert(false, "<Wwise> Unknown AudioEnvironmentImplementation type!");
+                }
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData or EnvironmentData passed to SetEnvironment");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::SetRtpc(
+        IATLAudioObjectData* const audioObjectData,
+        const IATLRtpcImplData* const rtpcData,
+        const float value)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+        auto const implRtpcData = static_cast<const SATLRtpcImplData_wwise*>(rtpcData);
+
+        if (implObjectData && implRtpcData)
+        {
+            auto akRtpcValue = static_cast<AkRtpcValue>(implRtpcData->m_fMult * value + implRtpcData->m_fShift);
+
+            const AKRESULT akResult = AK::SoundEngine::SetRTPCValue(implRtpcData->nAKID, akRtpcValue, implObjectData->nAKID);
+
+            if (IS_WWISE_OK(akResult))
+            {
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::SetRTPCValue returned AKRESULT %d", akResult);
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData or RtpcData passed to SetRtpc");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::SetSwitchState(
+        IATLAudioObjectData* const audioObjectData,
+        const IATLSwitchStateImplData* const switchStateData)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+        auto const implSwitchStateData = static_cast<const SATLSwitchStateImplData_wwise*>(switchStateData);
+
+        if (implObjectData && implSwitchStateData)
+        {
+            switch (implSwitchStateData->eType)
+            {
+                case eWST_SWITCH:
+                {
+                    const AkGameObjectID akObjectId = (implObjectData->bHasPosition ? implObjectData->nAKID : m_globalGameObjectID);
+
+                    const AKRESULT akResult = AK::SoundEngine::SetSwitch(
+                            implSwitchStateData->nAKSwitchID,
+                            implSwitchStateData->nAKStateID,
+                            akObjectId);
+
+                    if (IS_WWISE_OK(akResult))
+                    {
+                        result = EAudioRequestStatus::Success;
+                    }
+                    else
+                    {
+                        AZLOG_WARN("AK::SoundEngine::SetSwitch() returned AKRESULT %d", akResult);
+                    }
+                    break;
+                }
+                case eWST_STATE:
+                {
+                    const AKRESULT akResult = AK::SoundEngine::SetState(
+                            implSwitchStateData->nAKSwitchID,
+                            implSwitchStateData->nAKStateID);
+
+                    if (IS_WWISE_OK(akResult))
+                    {
+                        result = EAudioRequestStatus::Success;
+                    }
+                    else
+                    {
+                        AZLOG_WARN("AK::SoundEngine::SetState() returned AKRESULT %d", akResult);
+                    }
+                    break;
+                }
+                case eWST_RTPC:
+                {
+                    const AkGameObjectID akObjectId = implObjectData->nAKID;
+
+                    const AKRESULT akResult = AK::SoundEngine::SetRTPCValue(
+                            implSwitchStateData->nAKSwitchID,
+                            static_cast<AkRtpcValue>(implSwitchStateData->fRtpcValue),
+                            akObjectId);
+
+                    if (IS_WWISE_OK(akResult))
+                    {
+                        result = EAudioRequestStatus::Success;
+                    }
+                    else
+                    {
+                        AZLOG_WARN("AK::SoundEngine::SetRTPCValue() returned AKRESULT %d", akResult);
+                    }
+                    break;
+                }
+                case eWST_NONE:
+                {
+                    break;
+                }
+                default:
+                {
+                    AZ_Assert(false, "<Wwise> Unknown EWwiseSwitchType");
+                    break;
+                }
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData or SwitchStateData passed to SetSwitchState");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::SetObstructionOcclusion(
+        IATLAudioObjectData* const audioObjectData,
+        const float obstruction,
+        const float occlusion)
+    {
+        if (obstruction < ObstructionOcclusionMin || obstruction > ObstructionOcclusionMax)
+        {
+            AZLOG_WARN(
+                "Obstruction value %f is out of range, Obstruction should be in range [%f, %f]", obstruction, ObstructionOcclusionMin,
+                ObstructionOcclusionMax);
+        }
+
+        if (occlusion < ObstructionOcclusionMin || occlusion > ObstructionOcclusionMax)
+        {
+            AZLOG_WARN(
+                "Occlusion value %f is out of range, Occlusion should be in range [%f, %f]", occlusion, ObstructionOcclusionMin,
+                ObstructionOcclusionMax);
+        }
+
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+        if (implObjectData)
+        {
+            const AKRESULT akResult = AK::SoundEngine::SetObjectObstructionAndOcclusion(
+                    implObjectData->nAKID,
+                    m_defaultListenerGameObjectID,  // only set the obstruction/occlusion for the default listener for now
+                    static_cast<AkReal32>(obstruction),
+                    static_cast<AkReal32>(occlusion));
+
+            if (IS_WWISE_OK(akResult))
+            {
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::SetObjectObstructionAndOcclusion() returned AKRESULT %d", akResult);
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData passed to SetObjectObstructionAndOcclusion");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::SetListenerPosition(
+        IATLListenerData* const listenerData,
+        const SATLWorldPosition& newPosition)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implListenerData = static_cast<SATLListenerData_wwise*>(listenerData);
+
+        if (implListenerData)
+        {
+            AkListenerPosition akListenerPos;
+            ATLTransformToAkTransform(newPosition, akListenerPos);
+
+            const AKRESULT akResult = AK::SoundEngine::SetPosition(implListenerData->nAKListenerObjectId, akListenerPos);
+
+            if (IS_WWISE_OK(akResult))
+            {
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::SetPosition() returned AKRESULT %d", akResult);
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid ListenerData passed to SetListenerPosition");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::ResetRtpc(IATLAudioObjectData* const audioObjectData, const IATLRtpcImplData* const rtpcData)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+        auto const implRtpcDat = static_cast<const SATLRtpcImplData_wwise*>(rtpcData);
+
+        if (implObjectData && implRtpcDat)
+        {
+            const AKRESULT akResult = AK::SoundEngine::ResetRTPCValue(implRtpcDat->nAKID, implObjectData->nAKID);
+
+            if (IS_WWISE_OK(akResult))
+            {
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::ResetRTPCValue() returned AKRESULT %d", akResult);
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid AudioObjectData or RtpcData passed to ResetRtpc");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::RegisterInMemoryFile(SATLAudioFileEntryInfo* const fileEntryInfo)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        if (fileEntryInfo)
+        {
+            auto const implFileEntryData = static_cast<SATLAudioFileEntryData_wwise*>(fileEntryInfo->pImplData);
+
+            if (implFileEntryData)
+            {
+                AkBankID akBankId = AK_INVALID_BANK_ID;
+
+                const AKRESULT akResult = AK::SoundEngine::LoadBankMemoryView(
+                    fileEntryInfo->pFileData,
+                    aznumeric_cast<AkUInt32>(fileEntryInfo->nSize),
+                    akBankId);
+
+                if (IS_WWISE_OK(akResult))
+                {
+                    implFileEntryData->nAKBankID = akBankId;
+                    result = EAudioRequestStatus::Success;
+                }
+                else
+                {
+                    implFileEntryData->nAKBankID = AK_INVALID_BANK_ID;
+                    AZLOG_WARN("AK::SoundEngine::LoadBankMemoryView() returned AKRESULT %d", akResult);
+                }
+            }
+            else
+            {
+                AZLOG_ERROR("%s", "Invalid AudioFileEntryData passed to RegisterInMemoryFile");
+            }
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::UnregisterInMemoryFile(SATLAudioFileEntryInfo* const fileEntryInfo)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        if (fileEntryInfo)
+        {
+            auto const implFileEntryData = static_cast<SATLAudioFileEntryData_wwise*>(fileEntryInfo->pImplData);
+
+            if (implFileEntryData)
+            {
+                const AKRESULT akResult = AK::SoundEngine::UnloadBank(implFileEntryData->nAKBankID, fileEntryInfo->pFileData);
+
+                if (IS_WWISE_OK(akResult))
+                {
+                    result = EAudioRequestStatus::Success;
+                }
+                else
+                {
+                    AZLOG_WARN("AK::SoundEngine::UnloadBank() returned AKRESULT %d", akResult);
+                }
+            }
+            else
+            {
+                AZLOG_ERROR("%s", "Invalid AudioFileEntryData passed to UnregisterInMemoryFile");
+            }
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::ParseAudioFileEntry(const AZ::rapidxml::xml_node<char>* audioFileEntryNode, SATLAudioFileEntryInfo* const fileEntryInfo)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        if (audioFileEntryNode && azstricmp(audioFileEntryNode->name(), WwiseXmlTags::WwiseFileTag) == 0 && fileEntryInfo)
+        {
+            const char* audioFileEntryName = nullptr;
+            auto fileEntryNameAttr = audioFileEntryNode->first_attribute(WwiseXmlTags::WwiseNameAttribute, 0, false);
+            if (fileEntryNameAttr)
+            {
+                audioFileEntryName = fileEntryNameAttr->value();
+            }
+
+            bool isLocalized = false;
+            auto localizedAttr = audioFileEntryNode->first_attribute(WwiseXmlTags::WwiseLocalizedAttribute, 0, false);
+
+            // Legacy Preload support
+            if (!localizedAttr)
+            {
+                localizedAttr = audioFileEntryNode->first_attribute(WwiseXmlTags::Legacy::WwiseLocalizedAttribute, 0, false);
+            }
+
+            if (localizedAttr)
+            {
+                if (azstricmp(localizedAttr->value(), "true") == 0)
+                {
+                    isLocalized = true;
+                }
+            }
+
+            if (audioFileEntryName && audioFileEntryName[0] != '\0')
+            {
+                fileEntryInfo->bLocalized = isLocalized;
+                fileEntryInfo->sFileName = audioFileEntryName;
+                fileEntryInfo->nMemoryBlockAlignment = AK_BANK_PLATFORM_DATA_ALIGNMENT;
+                fileEntryInfo->pImplData = azcreate(SATLAudioFileEntryData_wwise, (), Audio::AudioImplAllocator);
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                fileEntryInfo->sFileName = nullptr;
+                fileEntryInfo->nMemoryBlockAlignment = 0;
+                fileEntryInfo->pImplData = nullptr;
+            }
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DeleteAudioFileEntryData(IATLAudioFileEntryData* const oldAudioFileEntry)
+    {
+        azdestroy(oldAudioFileEntry, Audio::AudioImplAllocator, SATLAudioFileEntryData_wwise);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    const char* const CAudioSystemImpl_wwise::GetAudioFileLocation(SATLAudioFileEntryInfo* const fileEntryInfo)
+    {
+        const char* location = nullptr;
+
+        if (fileEntryInfo)
+        {
+            location = fileEntryInfo->bLocalized ? m_localizedSoundbankFolder.c_str() : m_soundbankFolder.c_str();
+        }
+
+        return location;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    SATLAudioObjectData_wwise* CAudioSystemImpl_wwise::NewGlobalAudioObjectData(const TAudioObjectID objectId)
+    {
+        AZ_UNUSED(objectId);
+        auto newObjectData = azcreate(SATLAudioObjectData_wwise, (AK_INVALID_GAME_OBJECT, false), Audio::AudioImplAllocator);
+        return newObjectData;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    SATLAudioObjectData_wwise* CAudioSystemImpl_wwise::NewAudioObjectData(const TAudioObjectID objectId)
+    {
+        auto newObjectData = azcreate(SATLAudioObjectData_wwise, (static_cast<AkGameObjectID>(objectId), true), Audio::AudioImplAllocator);
+        return newObjectData;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DeleteAudioObjectData(IATLAudioObjectData* const oldObjectData)
+    {
+        azdestroy(oldObjectData, Audio::AudioImplAllocator, SATLAudioObjectData_wwise);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    SATLListenerData_wwise* CAudioSystemImpl_wwise::NewDefaultAudioListenerObjectData(const TATLIDType listenerId)
+    {
+        auto newObjectData = azcreate(SATLListenerData_wwise, (static_cast<AkGameObjectID>(listenerId)), Audio::AudioImplAllocator);
+        if (newObjectData)
+        {
+            auto listenerName = AZStd::string::format("DefaultAudioListener(%llu)", static_cast<AZ::u64>(newObjectData->nAKListenerObjectId));
+            AKRESULT akResult = AK::SoundEngine::RegisterGameObj(newObjectData->nAKListenerObjectId, listenerName.c_str());
+            if (IS_WWISE_OK(akResult))
+            {
+                akResult = AK::SoundEngine::SetDefaultListeners(&newObjectData->nAKListenerObjectId, 1);
+                if (IS_WWISE_OK(akResult))
+                {
+                    m_defaultListenerGameObjectID = newObjectData->nAKListenerObjectId;
+                }
+                else
+                {
+                    AZLOG_WARN("AK::SoundEngine::SetDefaultListeners() returned AKRESULT %d", akResult);
+                }
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::RegisterGameObj() returned AKRESULT %d", akResult);
+            }
+        }
+
+        return newObjectData;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    SATLListenerData_wwise* CAudioSystemImpl_wwise::NewAudioListenerObjectData(const TATLIDType listenerId)
+    {
+        auto newObjectData = azcreate(SATLListenerData_wwise, (static_cast<AkGameObjectID>(listenerId)), Audio::AudioImplAllocator);
+        if (newObjectData)
+        {
+            auto listenerName = AZStd::string::format("AudioListener(%llu)", static_cast<AZ::u64>(newObjectData->nAKListenerObjectId));
+            AKRESULT akResult = AK::SoundEngine::RegisterGameObj(newObjectData->nAKListenerObjectId, listenerName.c_str());
+            if (!IS_WWISE_OK(akResult))
+            {
+                AZLOG_WARN("AK::SoundEngine::RegisterGameObj() returned AKRESULT %d", akResult);
+            }
+        }
+
+        return newObjectData;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DeleteAudioListenerObjectData(IATLListenerData* const oldListenerData)
+    {
+        auto listenerData = static_cast<SATLListenerData_wwise*>(oldListenerData);
+        if (listenerData)
+        {
+            AKRESULT akResult = AK::SoundEngine::UnregisterGameObj(listenerData->nAKListenerObjectId);
+            if (IS_WWISE_OK(akResult))
+            {
+                if (listenerData->nAKListenerObjectId == m_defaultListenerGameObjectID)
+                {
+                    m_defaultListenerGameObjectID = AK_INVALID_GAME_OBJECT;
+                }
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::UnregisterGameObj() returned AKRESULT %d", akResult);
+            }
+        }
+
+        azdestroy(oldListenerData, Audio::AudioImplAllocator, SATLListenerData_wwise);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    SATLEventData_wwise* CAudioSystemImpl_wwise::NewAudioEventData(const TAudioEventID eventId)
+    {
+        auto newObjectData = azcreate(SATLEventData_wwise, (eventId), Audio::AudioImplAllocator);
+        return newObjectData;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DeleteAudioEventData(IATLEventData* const oldEventData)
+    {
+        azdestroy(oldEventData, Audio::AudioImplAllocator, SATLEventData_wwise);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::ResetAudioEventData(IATLEventData* const eventData)
+    {
+        auto const implEventData = static_cast<SATLEventData_wwise*>(eventData);
+
+        if (implEventData)
+        {
+            implEventData->audioEventState = eAES_NONE;
+            implEventData->nAKID = AK_INVALID_UNIQUE_ID;
+            implEventData->nSourceId = INVALID_AUDIO_SOURCE_ID;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    IATLTriggerImplData* CAudioSystemImpl_wwise::NewAudioTriggerImplData(const AZ::rapidxml::xml_node<char>* audioTriggerNode)
+    {
+        SATLTriggerImplData_wwise* newTriggerImpl = nullptr;
+
+        if (audioTriggerNode && azstricmp(audioTriggerNode->name(), WwiseXmlTags::WwiseEventTag) == 0)
+        {
+            auto eventNameAttr = audioTriggerNode->first_attribute(WwiseXmlTags::WwiseNameAttribute, 0, false);
+            if (eventNameAttr)
+            {
+                const char* eventName = eventNameAttr->value();
+                const AkUniqueID akId = AK::SoundEngine::GetIDFromString(eventName);
+
+                if (akId != AK_INVALID_UNIQUE_ID)
+                {
+                    newTriggerImpl = azcreate(SATLTriggerImplData_wwise, (akId), Audio::AudioImplAllocator);
+                }
+            }
+        }
+
+        return newTriggerImpl;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DeleteAudioTriggerImplData(IATLTriggerImplData* const oldTriggerImplData)
+    {
+        azdestroy(oldTriggerImplData, Audio::AudioImplAllocator, SATLTriggerImplData_wwise);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    IATLRtpcImplData* CAudioSystemImpl_wwise::NewAudioRtpcImplData(const AZ::rapidxml::xml_node<char>* audioRtpcNode)
+    {
+        SATLRtpcImplData_wwise* newRtpcImpl = nullptr;
+
+        AkRtpcID akRtpcId = AK_INVALID_RTPC_ID;
+        float mult = 1.f;
+        float shift = 0.f;
+
+        ParseRtpcImpl(audioRtpcNode, akRtpcId, mult, shift);
+
+        if (akRtpcId != AK_INVALID_RTPC_ID)
+        {
+            newRtpcImpl = azcreate(SATLRtpcImplData_wwise, (akRtpcId, mult, shift), Audio::AudioImplAllocator);
+        }
+
+        return newRtpcImpl;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DeleteAudioRtpcImplData(IATLRtpcImplData* const oldRtpcImplData)
+    {
+        azdestroy(oldRtpcImplData, Audio::AudioImplAllocator, SATLRtpcImplData_wwise);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    IATLSwitchStateImplData* CAudioSystemImpl_wwise::NewAudioSwitchStateImplData(const AZ::rapidxml::xml_node<char>* audioSwitchNode)
+    {
+        SATLSwitchStateImplData_wwise* newSwitchImpl = nullptr;
+        const char* nodeName = audioSwitchNode->name();
+
+        if (azstricmp(nodeName, WwiseXmlTags::WwiseSwitchTag) == 0)
+        {
+            newSwitchImpl = ParseWwiseSwitchOrState(audioSwitchNode, eWST_SWITCH);
+        }
+        else if (azstricmp(nodeName, WwiseXmlTags::WwiseStateTag) == 0)
+        {
+            newSwitchImpl = ParseWwiseSwitchOrState(audioSwitchNode, eWST_STATE);
+        }
+        else if (azstricmp(nodeName, WwiseXmlTags::WwiseRtpcSwitchTag) == 0)
+        {
+            newSwitchImpl = ParseWwiseRtpcSwitch(audioSwitchNode);
+        }
+
+        return newSwitchImpl;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DeleteAudioSwitchStateImplData(IATLSwitchStateImplData* const oldSwitchStateImplData)
+    {
+        azdestroy(oldSwitchStateImplData, Audio::AudioImplAllocator, SATLSwitchStateImplData_wwise);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    IATLEnvironmentImplData* CAudioSystemImpl_wwise::NewAudioEnvironmentImplData(const AZ::rapidxml::xml_node<char>* audioEnvironmentNode)
+    {
+        SATLEnvironmentImplData_wwise* newEnvironmentImpl = nullptr;
+
+        if (azstricmp(audioEnvironmentNode->name(), WwiseXmlTags::WwiseAuxBusTag) == 0)
+        {
+            auto auxBusNameAttr = audioEnvironmentNode->first_attribute(WwiseXmlTags::WwiseNameAttribute, 0, false);
+            if (auxBusNameAttr)
+            {
+                const char* auxBusName = auxBusNameAttr->value();
+                const AkUniqueID akBusId = AK::SoundEngine::GetIDFromString(auxBusName);
+
+                if (akBusId != AK_INVALID_AUX_ID)
+                {
+                    newEnvironmentImpl = azcreate(SATLEnvironmentImplData_wwise, (eWAET_AUX_BUS, static_cast<AkAuxBusID>(akBusId)), Audio::AudioImplAllocator);
+                }
+            }
+        }
+        else if (azstricmp(audioEnvironmentNode->name(), WwiseXmlTags::WwiseRtpcTag) == 0)
+        {
+            AkRtpcID akRtpcId = AK_INVALID_RTPC_ID;
+            float mult = 1.f;
+            float shift = 0.f;
+            ParseRtpcImpl(audioEnvironmentNode, akRtpcId, mult, shift);
+
+            if (akRtpcId != AK_INVALID_RTPC_ID)
+            {
+                newEnvironmentImpl = azcreate(SATLEnvironmentImplData_wwise, (eWAET_RTPC, akRtpcId, mult, shift), Audio::AudioImplAllocator);
+            }
+        }
+
+        return newEnvironmentImpl;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DeleteAudioEnvironmentImplData(IATLEnvironmentImplData* const oldEnvironmentImplData)
+    {
+        azdestroy(oldEnvironmentImplData, Audio::AudioImplAllocator, SATLEnvironmentImplData_wwise);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    const char* const CAudioSystemImpl_wwise::GetImplementationNameString() const
+    {
+#if !defined(WWISE_RELEASE)
+        return m_fullImplString.c_str();
+#else
+        return nullptr;
+#endif // !WWISE_RELEASE
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::GetMemoryInfo(SAudioImplMemoryInfo& memoryInfo) const
+    {
+        memoryInfo.nPrimaryPoolSize = AZ::AllocatorInstance<Audio::AudioImplAllocator>::Get().Capacity();
+        memoryInfo.nPrimaryPoolUsedSize = memoryInfo.nPrimaryPoolSize - AZ::AllocatorInstance<Audio::AudioImplAllocator>::Get().GetUnAllocatedMemory();
+        memoryInfo.nPrimaryPoolAllocations = 0;
+        memoryInfo.nSecondaryPoolSize = 0;
+        memoryInfo.nSecondaryPoolUsedSize = 0;
+        memoryInfo.nSecondaryPoolAllocations = 0;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    AZStd::vector<AudioImplMemoryPoolInfo> CAudioSystemImpl_wwise::GetMemoryPoolInfo()
+    {
+#if !defined(WWISE_RELEASE)
+        // Update memory category info...
+        for (auto& memInfo : m_debugMemoryInfo)
+        {
+            if (memInfo.m_poolId < 0)
+            {
+                break;
+            }
+
+            AK::MemoryMgr::CategoryStats categoryStats;
+            AK::MemoryMgr::GetCategoryStats(memInfo.m_poolId, categoryStats);
+
+            memInfo.m_memoryUsed = static_cast<AZ::u32>(categoryStats.uUsed);
+            memInfo.m_peakUsed = static_cast<AZ::u32>(categoryStats.uPeakUsed);
+            memInfo.m_numAllocs = categoryStats.uAllocs;
+            memInfo.m_numFrees = categoryStats.uFrees;
+        }
+
+        AK::MemoryMgr::GlobalStats globalStats;
+        AK::MemoryMgr::GetGlobalStats(globalStats);
+
+        auto& memInfo = m_debugMemoryInfo.back();
+        memInfo.m_memoryReserved = static_cast<AZ::u32>(globalStats.uReserved);
+        memInfo.m_memoryUsed = static_cast<AZ::u32>(globalStats.uUsed);
+        memInfo.m_peakUsed = static_cast<AZ::u32>(globalStats.uMax);
+
+        // return the memory infos...
+        return m_debugMemoryInfo;
+#else
+        return AZStd::vector<AudioImplMemoryPoolInfo>();
+#endif // !WWISE_RELEASE
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    bool CAudioSystemImpl_wwise::CreateAudioSource(const SAudioInputConfig& sourceConfig)
+    {
+        return AudioSourceManager::Get().CreateSource(sourceConfig);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::DestroyAudioSource(TAudioSourceId sourceId)
+    {
+        AudioSourceManager::Get().DestroySource(sourceId);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::SetPanningMode(PanningMode mode)
+    {
+        AkPanningRule panningRule;
+        switch (mode)
+        {
+        case PanningMode::Speakers:
+            panningRule = AkPanningRule_Speakers;
+            break;
+        case PanningMode::Headphones:
+            panningRule = AkPanningRule_Headphones;
+            break;
+        default:
+            return;
+        }
+
+        AKRESULT akResult = AK::SoundEngine::SetPanningRule(panningRule);
+        if (!IS_WWISE_OK(akResult))
+        {
+            AZLOG_WARN("AK::SoundEngine::SetPanningRule() returned AKRESULT %d", akResult);
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    SATLSwitchStateImplData_wwise* CAudioSystemImpl_wwise::ParseWwiseSwitchOrState(const AZ::rapidxml::xml_node<char>* node, EWwiseSwitchType type)
+    {
+        SATLSwitchStateImplData_wwise* switchStateImpl = nullptr;
+
+        auto switchNameAttr = node->first_attribute(WwiseXmlTags::WwiseNameAttribute, 0, false);
+        if (switchNameAttr)
+        {
+            const char* switchName = switchNameAttr->value();
+
+            auto valueNode = node->first_node(WwiseXmlTags::WwiseValueTag, 0, false);
+            if (valueNode)
+            {
+                auto valueNameAttr = valueNode->first_attribute(WwiseXmlTags::WwiseNameAttribute, 0, false);
+                if (valueNameAttr)
+                {
+                    const char* stateName = valueNameAttr->value();
+
+                    const AkUniqueID akSGroupId = AK::SoundEngine::GetIDFromString(switchName);
+                    const AkUniqueID akSNameId = AK::SoundEngine::GetIDFromString(stateName);
+
+                    if (akSGroupId != AK_INVALID_UNIQUE_ID && akSNameId != AK_INVALID_UNIQUE_ID)
+                    {
+                        switchStateImpl = azcreate(SATLSwitchStateImplData_wwise, (type, akSGroupId, akSNameId), Audio::AudioImplAllocator);
+                    }
+                }
+            }
+        }
+
+        return switchStateImpl;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    SATLSwitchStateImplData_wwise* CAudioSystemImpl_wwise::ParseWwiseRtpcSwitch(const AZ::rapidxml::xml_node<char>* node)
+    {
+        SATLSwitchStateImplData_wwise* switchStateImpl = nullptr;
+
+        if (node && azstricmp(node->name(), WwiseXmlTags::WwiseRtpcSwitchTag) == 0)
+        {
+            auto rtpcNameAttr = node->first_attribute(WwiseXmlTags::WwiseNameAttribute, 0, false);
+            if (rtpcNameAttr)
+            {
+                const char* rtpcName = rtpcNameAttr->value();
+                float rtpcValue = 0.f;
+
+                auto rtpcValueAttr = node->first_attribute(WwiseXmlTags::WwiseValueAttribute, 0, false);
+                if (rtpcValueAttr)
+                {
+                    rtpcValue = AZStd::stof(AZStd::string(rtpcValueAttr->value()));
+
+                    const AkUniqueID akRtpcId = AK::SoundEngine::GetIDFromString(rtpcName);
+                    if (akRtpcId != AK_INVALID_RTPC_ID)
+                    {
+                        switchStateImpl = azcreate(SATLSwitchStateImplData_wwise, (eWST_RTPC, akRtpcId, akRtpcId, rtpcValue), Audio::AudioImplAllocator);
+                    }
+                }
+            }
+        }
+
+        return switchStateImpl;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::ParseRtpcImpl(const AZ::rapidxml::xml_node<char>* node, AkRtpcID& akRtpcId, float& mult, float& shift)
+    {
+        if (node && azstricmp(node->name(), WwiseXmlTags::WwiseRtpcTag) == 0)
+        {
+            auto rtpcAttr = node->first_attribute(WwiseXmlTags::WwiseNameAttribute, 0, false);
+            if (rtpcAttr)
+            {
+                const char* rtpcName = rtpcAttr->value();
+                akRtpcId = static_cast<AkRtpcID>(AK::SoundEngine::GetIDFromString(rtpcName));
+
+                if (akRtpcId != AK_INVALID_RTPC_ID)
+                {
+                    auto multAttr = node->first_attribute(WwiseXmlTags::WwiseMutiplierAttribute, 0, false);
+                    if (multAttr)
+                    {
+                        mult = AZStd::stof(AZStd::string(multAttr->value()));
+                    }
+
+                    auto shiftAttr = node->first_attribute(WwiseXmlTags::WwiseShiftAttribute, 0, false);
+                    if (shiftAttr)
+                    {
+                        shift = AZStd::stof(AZStd::string(shiftAttr->value()));
+                    }
+                }
+            }
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::PrepUnprepTriggerSync(
+        const IATLTriggerImplData* const triggerData,
+        bool prepare)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+        auto const implTriggerData = static_cast<const SATLTriggerImplData_wwise*>(triggerData);
+
+        if (implTriggerData)
+        {
+            AkUniqueID akUniqueId = implTriggerData->nAKID;
+
+            const AKRESULT akResult = AK::SoundEngine::PrepareEvent(
+                    prepare ? AK::SoundEngine::Preparation_Load : AK::SoundEngine::Preparation_Unload,
+                    &akUniqueId,
+                    1);
+
+            if (IS_WWISE_OK(akResult))
+            {
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                AZLOG_WARN("AK::SoundEngine::PrepareEvent() returned AKRESULT %d", akResult);
+            }
+        }
+        else
+        {
+            AZLOG_ERROR("%s", "Invalid ATLTriggerData or EventData passed to PrepUnprepTriggerSync");
+        }
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::PrepUnprepTriggerAsync(
+        [[maybe_unused]] const IATLTriggerImplData* const triggerData,
+        [[maybe_unused]] IATLEventData* const eventData,
+        [[maybe_unused]] bool prepare)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+
+#if 0   // Turned off, PrepareEvent is not supported yet.
+        auto const implTriggerData = static_cast<const SATLTriggerImplData_wwise*>(triggerData);
+        auto const implEventData = static_cast<SATLEventData_wwise*>(eventData);
+
+        if (implTriggerData && implEventData)
+        {
+            AkUniqueID akUniqueId = implTriggerData->nAKID;
+
+            const AKRESULT akResult = AK::SoundEngine::PrepareEvent(
+                    prepare ? AK::SoundEngine::Preparation_Load : AK::SoundEngine::Preparation_Unload,
+                    &akUniqueId,
+                    1,
+                    &PrepareEventCallback,
+                    implEventData);
+
+            if (IS_WWISE_OK(akResult))
+            {
+                implEventData->nAKID = akUniqueId;
+                implEventData->audioEventState = eAES_UNLOADING;
+
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                g_audioImplLogger_wwise.Log(
+                    LogType::Warning,
+                    "Wwise PrepareEvent with %s failed for Wwise event %u with AKRESULT: %u",
+                    prepare ? "Preparation_Load" : "Preparation_Unload",
+                    akUniqueId,
+                    akResult);
+            }
+        }
+        else
+        {
+            g_audioImplLogger_wwise.Log(LogType::Error,
+                "Invalid ATLTriggerData or EventData passed to the Wwise implementation of %sTriggerAsync",
+                prepare ? "Prepare" : "Unprepare");
+        }
+#endif
+
+        return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::SetBankPaths()
+    {
+        // Default...
+        // "sounds/wwise/"
+        AZStd::string bankPath = Audio::Wwise::DefaultBanksPath;
+
+        // "sounds/wwise/wwise_config.json"
+        AZStd::string configFile = bankPath + Audio::Wwise::ConfigFile;
+
+        if (AZ::IO::FileIOBase::GetInstance()
+            && AZ::IO::FileIOBase::GetInstance()->Exists(configFile.c_str()))
+        {
+            Audio::Wwise::ConfigurationSettings configSettings;
+            if (configSettings.Load(configFile))
+            {
+                for (const auto& platformMap : configSettings.m_platformMappings)
+                {
+                    // May need to do a series of checks compare the data in the config settings to what's actually in the file system.
+                    // This is the most straightforward platform check.
+                    if (azstricmp(platformMap.m_enginePlatform.c_str(), AZ_TRAIT_OS_PLATFORM_NAME) == 0)
+                    {
+                        AZStd::string platformPath;
+                        // "sounds/wwise/windows"
+                        AZ::StringFunc::AssetDatabasePath::Join(bankPath.c_str(), platformMap.m_bankSubPath.c_str(), platformPath);
+
+                        AZStd::string initBankPath;
+                        // "sounds/wwise/windows/init.bnk"
+                        AZ::StringFunc::AssetDatabasePath::Join(platformPath.c_str(), Audio::Wwise::InitBank, initBankPath);
+                        if (AZ::IO::FileIOBase::GetInstance()->Exists(initBankPath.c_str()))
+                        {
+                            if (!platformPath.ends_with(AZ_CORRECT_DATABASE_SEPARATOR))
+                            {
+                                platformPath.push_back(AZ_CORRECT_DATABASE_SEPARATOR);
+                            }
+                            bankPath = AZStd::move(platformPath);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        m_soundbankFolder = bankPath;
+        m_localizedSoundbankFolder = bankPath;
+
+        Audio::Wwise::SetBanksRootPath(m_soundbankFolder);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    bool CAudioSystemImpl_wwise::SEnvPairCompare::operator()(const AZStd::pair<const AkAuxBusID, float>& pair1, const AZStd::pair<const AkAuxBusID, float>& pair2) const
+    {
+        return (pair1.second > pair2.second);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    EAudioRequestStatus CAudioSystemImpl_wwise::PostEnvironmentAmounts(IATLAudioObjectData* const audioObjectData)
+    {
+        EAudioRequestStatus result = EAudioRequestStatus::Failure;
+        auto const implObjectData = static_cast<SATLAudioObjectData_wwise*>(audioObjectData);
+
+        if (implObjectData)
+        {
+            AkAuxSendValue akAuxSendValues[LY_MAX_AUX_PER_OBJ];
+            AZ::u32 auxCount = 0;
+
+            SATLAudioObjectData_wwise::TEnvironmentImplMap::iterator envPair = implObjectData->cEnvironmentImplAmounts.begin();
+            const SATLAudioObjectData_wwise::TEnvironmentImplMap::const_iterator envBegin = implObjectData->cEnvironmentImplAmounts.begin();
+            const SATLAudioObjectData_wwise::TEnvironmentImplMap::const_iterator envEnd = implObjectData->cEnvironmentImplAmounts.end();
+
+            if (implObjectData->cEnvironmentImplAmounts.size() <= LY_MAX_AUX_PER_OBJ)
+            {
+                for (; envPair != envEnd; ++auxCount)
+                {
+                    const float amount = envPair->second;
+
+                    akAuxSendValues[auxCount].auxBusID = envPair->first;
+                    akAuxSendValues[auxCount].fControlValue = amount;
+                    akAuxSendValues[auxCount].listenerID = m_defaultListenerGameObjectID;  // TODO: Expand api to allow specify listeners
+
+                    // If an amount is zero, we still want to send it to the middleware, but we also want to remove it from the map.
+                    if (amount == 0.0f)
+                    {
+                        envPair = implObjectData->cEnvironmentImplAmounts.erase(envPair);
+                    }
+                    else
+                    {
+                        ++envPair;
+                    }
+                }
+            }
+            else
+            {
+                // sort the environments in order of decreasing amounts and take the first LY_MAX_AUX_PER_OBJ worth
+                using TEnvPairSet = AZStd::set<SATLAudioObjectData_wwise::TEnvironmentImplMap::value_type, SEnvPairCompare, Audio::AudioImplStdAllocator>;
+                TEnvPairSet envPairs(envBegin, envEnd);
+
+                TEnvPairSet::const_iterator sortedEnvPair = envPairs.begin();
+                const TEnvPairSet::const_iterator sortedEnvEnd = envPairs.end();
+
+                for (; (sortedEnvPair != sortedEnvEnd) && (auxCount < LY_MAX_AUX_PER_OBJ); ++sortedEnvPair, ++auxCount)
+                {
+                    akAuxSendValues[auxCount].auxBusID = sortedEnvPair->first;
+                    akAuxSendValues[auxCount].fControlValue = sortedEnvPair->second;
+                    akAuxSendValues[auxCount].listenerID = m_defaultListenerGameObjectID;      // TODO: Expand api to allow specify listeners
+                }
+
+                // remove all Environments with 0.0 amounts
+                while (envPair != envEnd)
+                {
+                    if (envPair->second == 0.0f)
+                    {
+                        envPair = implObjectData->cEnvironmentImplAmounts.erase(envPair);
+                    }
+                    else
+                    {
+                        ++envPair;
+                    }
+                }
+            }
+
+            AZ_Assert(auxCount <= LY_MAX_AUX_PER_OBJ, "WwiseImpl PostEnvironmentAmounts - Exceeded the allowed number of aux environments that can be set!");
+
+            const AKRESULT akResult = AK::SoundEngine::SetGameObjectAuxSendValues(implObjectData->nAKID, akAuxSendValues, auxCount);
+
+            if (IS_WWISE_OK(akResult))
+            {
+                result = EAudioRequestStatus::Success;
+            }
+            else
+            {
+                AZLOG_WARN(
+                    "AK::SoundEngine::SetGameObjectAuxSendValues() on object %llu returned AKRESULT %u",
+                    static_cast<AZ::u64>(implObjectData->nAKID), akResult);
+            }
+
+            implObjectData->bNeedsToUpdateEnvironments = false;
+        }
+
+        return result;
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    const char* const CAudioSystemImpl_wwise::GetImplSubPath() const
+    {
+        return WwiseImplSubPath;
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    void CAudioSystemImpl_wwise::SetLanguage(const char* const language)
+    {
+        if (language)
+        {
+            AZStd::string languageSubfolder(language);
+
+            languageSubfolder += "/";
+
+            m_localizedSoundbankFolder = m_soundbankFolder;
+            m_localizedSoundbankFolder.append(languageSubfolder);
+
+            m_fileIOHandler.SetLanguageFolder(languageSubfolder.c_str());
+        }
+    }
+
+} // namespace Audio

+ 195 - 0
Gems/AudioEngineWwise/Code/Source/Engine/AudioSystemImpl_wwise.h

@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#pragma once
+
+#include <AudioAllocators.h>
+#include <FileIOHandler_wwise.h>
+#include <ATLEntities_wwise.h>
+#include <IAudioSystemImplementation.h>
+
+
+namespace Audio
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    class CAudioSystemImpl_wwise
+        : public AudioSystemImplementation
+    {
+    public:
+        AUDIO_IMPL_CLASS_ALLOCATOR(CAudioSystemImpl_wwise)
+
+        explicit CAudioSystemImpl_wwise(const char* assetsPlatformName);
+        ~CAudioSystemImpl_wwise() override;
+
+        // AudioSystemImplementationNotificationBus
+        void OnAudioSystemLoseFocus() override;
+        void OnAudioSystemGetFocus() override;
+        void OnAudioSystemMuteAll() override;
+        void OnAudioSystemUnmuteAll() override;
+        void OnAudioSystemRefresh() override;
+        // ~AudioSystemImplementationNotificationBus
+
+        // AudioSystemImplementationRequestBus
+        void Update(const float updateIntervalMS) override;
+
+        EAudioRequestStatus Initialize() override;
+        EAudioRequestStatus ShutDown() override;
+        EAudioRequestStatus Release() override;
+
+        EAudioRequestStatus StopAllSounds() override;
+
+        EAudioRequestStatus RegisterAudioObject(
+            IATLAudioObjectData* const audioObjectData,
+            const char* const objectName) override;
+        EAudioRequestStatus UnregisterAudioObject(IATLAudioObjectData* const audioObjectData) override;
+        EAudioRequestStatus ResetAudioObject(IATLAudioObjectData* const audioObjectData) override;
+        EAudioRequestStatus UpdateAudioObject(IATLAudioObjectData* const audioObjectData) override;
+
+        EAudioRequestStatus PrepareTriggerSync(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLTriggerImplData* const triggerData) override;
+        EAudioRequestStatus UnprepareTriggerSync(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLTriggerImplData* const triggerData) override;
+        EAudioRequestStatus PrepareTriggerAsync(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLTriggerImplData* const triggerData,
+            IATLEventData* const eventData) override;
+        EAudioRequestStatus UnprepareTriggerAsync(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLTriggerImplData* const triggerData,
+            IATLEventData* const eventData) override;
+        EAudioRequestStatus ActivateTrigger(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLTriggerImplData* const triggerData,
+            IATLEventData* const eventData,
+            const SATLSourceData* const pSourceData) override;
+        EAudioRequestStatus StopEvent(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLEventData* const eventData) override;
+        EAudioRequestStatus StopAllEvents(
+            IATLAudioObjectData* const audioObjectData) override;
+        EAudioRequestStatus SetPosition(
+            IATLAudioObjectData* const audioObjectData,
+            const SATLWorldPosition& worldPosition) override;
+        EAudioRequestStatus SetMultiplePositions(
+            IATLAudioObjectData* const audioObjectData,
+            const MultiPositionParams& multiPositionParams) override;
+        EAudioRequestStatus SetEnvironment(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLEnvironmentImplData* const environmentData,
+            const float amount) override;
+        EAudioRequestStatus SetRtpc(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLRtpcImplData* const rtpcData,
+            const float value) override;
+        EAudioRequestStatus SetSwitchState(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLSwitchStateImplData* const switchStateData) override;
+        EAudioRequestStatus SetObstructionOcclusion(
+            IATLAudioObjectData* const audioObjectData,
+            const float obstruction,
+            const float occlusion) override;
+        EAudioRequestStatus SetListenerPosition(
+            IATLListenerData* const listenerData,
+            const SATLWorldPosition& newPosition) override;
+        EAudioRequestStatus ResetRtpc(
+            IATLAudioObjectData* const audioObjectData,
+            const IATLRtpcImplData* const rtpcData) override;
+
+        EAudioRequestStatus RegisterInMemoryFile(SATLAudioFileEntryInfo* const audioFileEntry) override;
+        EAudioRequestStatus UnregisterInMemoryFile(SATLAudioFileEntryInfo* const audioFileEntry) override;
+
+        EAudioRequestStatus ParseAudioFileEntry(const AZ::rapidxml::xml_node<char>* audioFileEntryNode, SATLAudioFileEntryInfo* const fileEntryInfo) override;
+        void DeleteAudioFileEntryData(IATLAudioFileEntryData* const oldAudioFileEntryData) override;
+        const char* const GetAudioFileLocation(SATLAudioFileEntryInfo* const fileEntryInfo) override;
+
+        IATLTriggerImplData* NewAudioTriggerImplData(const AZ::rapidxml::xml_node<char>* audioTriggerNode) override;
+        void DeleteAudioTriggerImplData(IATLTriggerImplData* const oldTriggerImplData) override;
+
+        IATLRtpcImplData* NewAudioRtpcImplData(const AZ::rapidxml::xml_node<char>* audioRtpcNode) override;
+        void DeleteAudioRtpcImplData(IATLRtpcImplData* const oldRtpcImplData) override;
+
+        IATLSwitchStateImplData* NewAudioSwitchStateImplData(const AZ::rapidxml::xml_node<char>* audioSwitchStateNode) override;
+        void DeleteAudioSwitchStateImplData(IATLSwitchStateImplData* const oldSwitchStateImplData) override;
+
+        IATLEnvironmentImplData* NewAudioEnvironmentImplData(const AZ::rapidxml::xml_node<char>* audioEnvironmentNode) override;
+        void DeleteAudioEnvironmentImplData(IATLEnvironmentImplData* const oldEnvironmentImplData) override;
+
+        SATLAudioObjectData_wwise* NewGlobalAudioObjectData(const TAudioObjectID objectId) override;
+        SATLAudioObjectData_wwise* NewAudioObjectData(const TAudioObjectID objectId) override;
+        void DeleteAudioObjectData(IATLAudioObjectData* const oldObjectData) override;
+
+        SATLListenerData_wwise* NewDefaultAudioListenerObjectData(const TATLIDType objectId) override;
+        SATLListenerData_wwise* NewAudioListenerObjectData(const TATLIDType objectId) override;
+        void DeleteAudioListenerObjectData(IATLListenerData* const oldListenerData) override;
+
+        SATLEventData_wwise* NewAudioEventData(const TAudioEventID eventId) override;
+        void DeleteAudioEventData(IATLEventData* const oldEventData) override;
+        void ResetAudioEventData(IATLEventData* const eventData) override;
+
+        const char* const GetImplSubPath() const override;
+        void SetLanguage(const char* const language) override;
+
+        // Functions below are only used when WWISE_RELEASE is not defined
+        const char* const GetImplementationNameString() const override;
+        void GetMemoryInfo(SAudioImplMemoryInfo& memoryInfo) const override;
+        AZStd::vector<AudioImplMemoryPoolInfo> GetMemoryPoolInfo() override;
+
+        bool CreateAudioSource(const SAudioInputConfig& sourceConfig) override;
+        void DestroyAudioSource(TAudioSourceId sourceId) override;
+
+        void SetPanningMode(PanningMode mode) override;
+        // ~AudioSystemImplementationRequestBus
+
+    protected:
+        void SetBankPaths();
+
+        AZStd::string m_soundbankFolder;
+        AZStd::string m_localizedSoundbankFolder;
+        AZStd::string m_assetsPlatform;
+
+    private:
+        static const char* const WwiseImplSubPath;
+        static const char* const WwiseGlobalAudioObjectName;
+        static const float ObstructionOcclusionMin;
+        static const float ObstructionOcclusionMax;
+
+        struct SEnvPairCompare
+        {
+            bool operator()(const AZStd::pair<const AkAuxBusID, float>& pair1, const AZStd::pair<const AkAuxBusID, float>& pair2) const;
+        };
+
+        SATLSwitchStateImplData_wwise* ParseWwiseSwitchOrState(const AZ::rapidxml::xml_node<char>* node, EWwiseSwitchType type);
+        SATLSwitchStateImplData_wwise* ParseWwiseRtpcSwitch(const AZ::rapidxml::xml_node<char>* node);
+        void ParseRtpcImpl(const AZ::rapidxml::xml_node<char>* node, AkRtpcID& akRtpcId, float& mult, float& shift);
+
+        EAudioRequestStatus PrepUnprepTriggerSync(
+            const IATLTriggerImplData* const triggerData,
+            bool prepare);
+        EAudioRequestStatus PrepUnprepTriggerAsync(
+            const IATLTriggerImplData* const triggerData,
+            IATLEventData* const eventData,
+            bool prepare);
+
+        EAudioRequestStatus PostEnvironmentAmounts(IATLAudioObjectData* const audioObjectData);
+
+        AkGameObjectID m_globalGameObjectID;
+        AkGameObjectID m_defaultListenerGameObjectID;
+        AkBankID m_initBankID;
+        CFileIOHandler_wwise m_fileIOHandler;
+
+#if !defined(WWISE_RELEASE)
+        bool m_isCommSystemInitialized;
+        AZStd::vector<AudioImplMemoryPoolInfo> m_debugMemoryInfo;
+        AZStd::string m_fullImplString;
+        AZStd::string m_speakerConfigString;
+#endif // !WWISE_RELEASE
+    };
+} // namespace Audio

+ 10 - 0
Gems/AudioEngineWwise/Code/Source/Engine/Common_wwise.cpp

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#include <Common_wwise.h>

+ 113 - 0
Gems/AudioEngineWwise/Code/Source/Engine/Common_wwise.h

@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#pragma once
+
+#include <AK/SoundEngine/Common/AkMemoryMgr.h>
+#include <AK/SoundEngine/Common/AkTypes.h>
+#include <AK/AkWwiseSDKVersion.h>
+#include <IAudioSystem.h>
+#include <AudioEngineWwise_Traits_Platform.h>
+
+#define WWISE_IMPL_VERSION_STRING   "Wwise " AK_WWISESDK_VERSIONNAME
+
+#define ASSERT_WWISE_OK(x) (AKASSERT((x) == AK_Success))
+#define IS_WWISE_OK(x)     ((x) == AK_Success)
+
+
+namespace Audio
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Wwise Xml Element Names
+    namespace WwiseXmlTags
+    {
+        static constexpr const char* WwiseEventTag = "WwiseEvent";
+        static constexpr const char* WwiseRtpcTag = "WwiseRtpc";
+        static constexpr const char* WwiseSwitchTag = "WwiseSwitch";
+        static constexpr const char* WwiseStateTag = "WwiseState";
+        static constexpr const char* WwiseRtpcSwitchTag = "WwiseRtpc";
+        static constexpr const char* WwiseFileTag = "WwiseFile";
+        static constexpr const char* WwiseAuxBusTag = "WwiseAuxBus";
+        static constexpr const char* WwiseValueTag = "WwiseValue";
+        static constexpr const char* WwiseNameAttribute = "wwise_name";
+        static constexpr const char* WwiseValueAttribute = "wwise_value";
+        static constexpr const char* WwiseMutiplierAttribute = "atl_mult";
+        static constexpr const char* WwiseShiftAttribute = "atl_shift";
+        static constexpr const char* WwiseLocalizedAttribute = "wwise_localized";
+
+        namespace Legacy
+        {
+            static constexpr const char* WwiseLocalizedAttribute = "wwise_localised";
+        }
+
+    } // namespace WwiseXmlTags
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Wwise-specific helper functions
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    inline AkVector AZVec3ToAkVector(const AZ::Vector3& vec3)
+    {
+        // swizzle Y <--> Z
+        AkVector akVec;
+        akVec.X = vec3.GetX();
+        akVec.Y = vec3.GetZ();
+        akVec.Z = vec3.GetY();
+        return akVec;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    inline AkTransform AZVec3ToAkTransform(const AZ::Vector3& position)
+    {
+        AkTransform akTransform;
+        akTransform.SetOrientation(0.0, 0.0, 1.0, 0.0, 1.0, 0.0);   // May add orientation support later.
+        akTransform.SetPosition(AZVec3ToAkVector(position));
+        return akTransform;
+    }
+
+#if AK_WWISESDK_VERSION_MAJOR >= 2022
+    inline void ATLTransformToAkTransform(const SATLWorldPosition& atlTransform, AkWorldTransform& akWorldTransform)
+    {
+        akWorldTransform.Set(
+            AZVec3ToAkVector(atlTransform.GetPositionVec()),
+            AZVec3ToAkVector(atlTransform.GetForwardVec().GetNormalized()), // Wwise SDK requires that the Orientation vectors
+            AZVec3ToAkVector(atlTransform.GetUpVec().GetNormalized())       // are normalized prior to sending to the apis.
+        );
+    }
+#endif // AK_WWISESDK_VERSION_MAJOR >= 2022
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    inline void ATLTransformToAkTransform(const SATLWorldPosition& atlTransform, AkTransform& akTransform)
+    {
+        akTransform.Set(
+            AZVec3ToAkVector(atlTransform.GetPositionVec()),
+            AZVec3ToAkVector(atlTransform.GetForwardVec().GetNormalized()), // Wwise SDK requires that the Orientation vectors
+            AZVec3ToAkVector(atlTransform.GetUpVec().GetNormalized())       // are normalized prior to sending to the apis.
+        );
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    namespace Wwise
+    {
+        // See AkMemoryMgr.h
+        inline static const char* MemoryManagerCategories[]
+        {
+            "Object", "Event", "Structure", "Media", "GameObject", "Processing", "ProcessingPlugin", "Streaming", "StreamingIO",
+            "SpatialAudio", "SpatialAudioGeometry", "SpatialAudioPaths", "GameSim", "MonitorQueue", "Profiler", "FilePackage",
+            "SoundEngine", "Integration"
+#if AK_WWISESDK_VERSION_MAJOR >= 2022
+            , "JobMgr"
+#endif // AK_WWISESDK_VERSION_MAJOR >= 2022
+        };
+
+        static_assert(AZ_ARRAY_SIZE(MemoryManagerCategories) == AkMemID_NUM,
+            "Wwise memory categories have changed, the list of display names needs to be updated.");
+    } // namespace Wwise
+
+} // namespace Audio

+ 106 - 0
Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.cpp

@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <Config_wwise.h>
+
+#include <AzCore/Component/ComponentApplicationBus.h>
+#include <AzCore/IO/Path/Path.h>
+#include <AzCore/Serialization/Json/JsonSerialization.h>
+#include <AzCore/Serialization/Json/JsonUtils.h>
+#include <AzCore/Serialization/SerializeContext.h>
+
+// For AZ_Printf statements...
+#define WWISE_CONFIG_WINDOW "WwiseConfig"
+
+
+namespace Audio::Wwise
+{
+    static AZStd::string_view s_configuredBanksPath = DefaultBanksPath;
+
+    const AZStd::string_view GetBanksRootPath()
+    {
+        return s_configuredBanksPath;
+    }
+
+    void SetBanksRootPath(const AZStd::string_view path)
+    {
+        s_configuredBanksPath = path;
+    }
+
+    // static
+    void ConfigurationSettings::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<PlatformMapping>()
+                ->Version(2)
+                ->Field("assetPlatform", &PlatformMapping::m_assetPlatform)
+                ->Field("altAssetPlatform", &PlatformMapping::m_altAssetPlatform)
+                ->Field("enginePlatform", &PlatformMapping::m_enginePlatform)
+                ->Field("wwisePlatform", &PlatformMapping::m_wwisePlatform)
+                ->Field("bankSubPath", &PlatformMapping::m_bankSubPath)
+                ;
+
+            serializeContext->Class<ConfigurationSettings>()
+                ->Version(1)
+                ->Field("platformMaps", &ConfigurationSettings::m_platformMappings)
+                ;
+        }
+    }
+
+    bool ConfigurationSettings::Load(const AZStd::string& filePath)
+    {
+        AZ::IO::Path fileIoPath(filePath);
+        auto outcome = AZ::JsonSerializationUtils::ReadJsonFile(fileIoPath.Native());
+        if (!outcome)
+        {
+            AZ_Printf(WWISE_CONFIG_WINDOW, "ERROR: %s\n", outcome.GetError().c_str());
+            return false;
+        }
+
+        m_platformMappings.clear();
+
+        AZ::JsonDeserializerSettings deserializeSettings;
+        AZ::ComponentApplicationBus::BroadcastResult(deserializeSettings.m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
+
+        auto result = AZ::JsonSerialization::Load(*this, outcome.GetValue(), deserializeSettings);
+        if (result.GetProcessing() != AZ::JsonSerializationResult::Processing::Completed)
+        {
+            AZ_Printf(WWISE_CONFIG_WINDOW, "ERROR: Deserializing Json file '%s'\n", filePath.c_str());
+            return false;
+        }
+
+        AZ_Printf(WWISE_CONFIG_WINDOW, "Loaded '%s' successfully.\n", filePath.c_str());
+        return true;
+    }
+
+    bool ConfigurationSettings::Save(const AZStd::string& filePath)
+    {
+        AZ::JsonSerializerSettings serializeSettings;
+        AZ::ComponentApplicationBus::BroadcastResult(serializeSettings.m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
+
+        rapidjson::Document jsonDoc;
+        auto result = AZ::JsonSerialization::Store(jsonDoc, jsonDoc.GetAllocator(), *this, serializeSettings);
+        if (result.GetProcessing() != AZ::JsonSerializationResult::Processing::Completed)
+        {
+            AZ_Printf(WWISE_CONFIG_WINDOW, "ERROR: Serializing Json file '%s'\n", filePath.c_str());
+            return false;
+        }
+
+        auto outcome = AZ::JsonSerializationUtils::WriteJsonFile(jsonDoc, filePath);
+        if (!outcome)
+        {
+            AZ_Printf(WWISE_CONFIG_WINDOW, "ERROR: %s\n", outcome.GetError().c_str());
+            return false;
+        }
+
+        AZ_Printf(WWISE_CONFIG_WINDOW, "Saved '%s' successfully.\n", filePath.c_str());
+        return true;
+    }
+
+} // namespace Audio::Wwise

+ 67 - 0
Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h

@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/PlatformIncl.h> // This include is needed to include WinSock2.h before including Windows.h
+                                 // As AK/SoundEngine/Common/AkTypes.h eventually includes Windows.h
+
+#include <AzCore/RTTI/ReflectContext.h>
+#include <AzCore/std/string/string.h>
+
+namespace Audio::Wwise
+{
+    static constexpr const char DefaultBanksPath[] = "sounds/wwise/";
+    static constexpr const char ExternalSourcesPath[] = "external";
+    static constexpr const char ConfigFile[] = "wwise_config.json";
+    static constexpr const char BankExtension[] = ".bnk";
+    static constexpr const char MediaExtension[] = ".wem";
+    static constexpr const char InitBank[] = "init.bnk";
+
+    //! Banks path that's set after reading the configuration settings.
+    //! This might be different than the DefaultBanksPath.
+    const AZStd::string_view GetBanksRootPath();
+    void SetBanksRootPath(const AZStd::string_view path);
+
+    /**
+     * ConfigurationSettings
+     */
+    struct ConfigurationSettings
+    {
+        AZ_TYPE_INFO(ConfigurationSettings, "{6BEEC05E-C5AE-4270-AAAD-08E27A6B5341}");
+        AZ_CLASS_ALLOCATOR(ConfigurationSettings, AZ::SystemAllocator);
+
+        struct PlatformMapping
+        {
+            AZ_TYPE_INFO(PlatformMapping, "{9D444546-784B-4509-A8A5-8E174E345097}");
+            AZ_CLASS_ALLOCATOR(PlatformMapping, AZ::SystemAllocator);
+
+            PlatformMapping() = default;
+            ~PlatformMapping() = default;
+
+            // Serialized Data...
+            AZStd::string m_assetPlatform;      // LY Asset Platform name (i.e. "pc", "mac", "android", ...)
+            AZStd::string m_altAssetPlatform;   // Some platforms can be run using a different asset platform.  Useful for builder worker.
+            AZStd::string m_enginePlatform;     // LY Engine Platform name (i.e. "Windows", "Mac", "Android", ...)
+            AZStd::string m_wwisePlatform;      // Wwise Platform name (i.e. "Windows", "Mac", "Android", ...)
+            AZStd::string m_bankSubPath;        // Wwise Banks Sub-Path (i.e. "windows", "mac", "android", ...)
+        };
+
+        ConfigurationSettings() = default;
+        ~ConfigurationSettings() = default;
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        bool Load(const AZStd::string& filePath);
+        bool Save(const AZStd::string& filePath);
+
+        // Serialized Data...
+        AZStd::vector<PlatformMapping> m_platformMappings;
+    };
+
+} // namespace Audio::Wwise

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません