The first step in integrating the Wwise sound engine is to properly initialize the various modules that make up the sound engine when the game starts up.
In this sample project, the following function is defined to perform everything related to initialization of the sound engine:
bool InitSoundEngine() { ... Initialization will be done here ... return true; }
It is called at the beginning of the main function. Let's look at the various items that need initializing:
Every project that links with the Wwise sound engine must define several hooks. These can then be used to monitor calls to memory access methods.
// Custom alloc/free functions. These are declared as "extern" in AkMemoryMgr.h // and MUST be defined by the game developer. namespace AK { #ifdef WIN32 void * AllocHook( size_t in_size ) { return malloc( in_size ); } void FreeHook( void * in_ptr ) { free( in_ptr ); } // Note: VirtualAllocHook() may be used by I/O pools of the default implementation // of the Stream Manager, to allow "true" unbuffered I/O (using FILE_FLAG_NO_BUFFERING // - refer to the Windows SDK documentation for more details). This is NOT mandatory; // you may implement it with a simple malloc(). void * VirtualAllocHook( void * in_pMemAddress, size_t in_size, DWORD in_dwAllocationType, DWORD in_dwProtect ) { return VirtualAlloc( in_pMemAddress, in_size, in_dwAllocationType, in_dwProtect ); } void VirtualFreeHook( void * in_pMemAddress, size_t in_size, DWORD in_dwFreeType ) { VirtualFree( in_pMemAddress, in_size, in_dwFreeType ); } #endif }
The first thing you must initialize is the Memory Manager. The following code creates the default Memory Manager with twenty memory pools:
#include <AK/SoundEngine/Common/AkMemoryMgr.h> // Memory Manager #include <AK/SoundEngine/Common/AkModule.h> // Default memory and stream managers (...) bool InitSoundEngine() { // // Create and initialize an instance of the default memory manager. Note // that you can override the default memory manager with your own. Refer // to the SDK documentation for more information. // AkMemSettings memSettings; memSettings.uMaxNumPools = 20; if ( AK::MemoryMgr::Init( &memSettings ) != AK_Success ) { assert( ! "Could not create the memory manager." ); return false; } (...) }
|
Note: You can override the Memory Manager, in which case the code presented above must be adapted as needed. Refer to Overriding the memory manager for more information. |
Refer to Memory Manager for more details regarding the Memory Manager.
Once the Memory Manager has been initialized, we can move on to the Streaming Manager.
The following code initializes the default Streaming Manager. It requires an instance of AK::StreamMgr::IAkFileLocationResolver, and creates a streaming device. This requires an instance of AK::StreamMgr::IAkIOHookBlocking or AK::StreamMgr::IAkIOHookDeferred, depending on the streaming device scheduler type (AK_DEVICE_BLOCKING or AK_DEVICE_DEFERRED_LINED_UP). These interfaces are defined in AkStreamMgrModule.h, which contains all definitions that are specific to the default Stream Manager implementation provided with the SDK.
The recommended way of integrating the sound engine with regards to disk I/O is to use the default Stream Manager implementation, and implement the interfaces defined in AkStreamMgrModule.h. These interfaces constitute the Low-Level I/O submodule. Refer to Streaming / Stream Manager for more information.
This example uses the sample CAkFilePackageLowLevelIOBlocking as is. This class implements both AK::StreamMgr::IAkFileLocationResolver and AK::StreamMgr::IAkIOHookBlocking interfaces, and is able to load file packages generated with the File Packager utility (refer to File Package Low-Level I/O Implementation and File Packager Utility for more information about the File Packager and how it works in the Low-Level I/O).
Refer to Low-Level I/O for more information about the Low-Level I/O.
#include <AK/SoundEngine/Common/IAkStreamMgr.h> // Streaming Manager #include <AK/Tools/Common/AkPlatformFuncs.h> // Thread defines #include <AkFilePackageLowLevelIOBlocking.h> // Sample low-level I/O implementation (...) // We're using the default Low-Level I/O implementation that's part // of the SDK's sample code, with the file package extension CAkFilePackageLowLevelIOBlocking g_lowLevelIO; (...) bool InitSoundEngine() { (...) // // Create and initialize an instance of the default streaming manager. Note // that you can override the default streaming manager with your own. Refer // to the SDK documentation for more information. // AkStreamMgrSettings stmSettings; AK::StreamMgr::GetDefaultSettings( stmSettings ); // Customize the Stream Manager settings here. if ( !AK::StreamMgr::Create( stmSettings ) ) { assert( ! "Could not create the Streaming Manager" ); return false; } // // Create a streaming device with blocking low-level I/O handshaking. // Note that you can override the default low-level I/O module with your own. Refer // to the SDK documentation for more information. // AkDeviceSettings deviceSettings; AK::StreamMgr::GetDefaultDeviceSettings( deviceSettings ); // Customize the streaming device settings here. // CAkFilePackageLowLevelIOBlocking::Init() creates a streaming device // in the Stream Manager, and registers itself as the File Location Resolver. if ( g_lowLevelIO.Init( deviceSettings ) != AK_Success ) { assert( ! "Could not create the streaming device and Low-Level I/O system" ); return false; } (...) }
For more information about the initialization settings of the default Stream Manager and streaming devices, refer to Audiokinetic Stream Manager Initialization Settings.
|
Caution: Some of the initialization settings use member structures that are platform specific, like the AkThreadProperties. |
If you decide to override the default Low-Level IO implementation, or the complete Streaming Manager, this code will need to be adapted accordingly. Refer to Streaming / Stream Manager for more information.
Now that the basic modules have been initialized, we're ready to initialize the sound engine itself:
#include <AK/SoundEngine/Common/AkSoundEngine.h> // Sound engine (...) bool InitSoundEngine() { (...) // // Create the Sound Engine // Using default initialization parameters // AkInitSettings initSettings; AkPlatformInitSettings platformInitSettings; AK::SoundEngine::GetDefaultInitSettings( initSettings ); AK::SoundEngine::GetDefaultPlatformInitSettings( platformInitSettings ); if ( AK::SoundEngine::Init( &initSettings, &platformInitSettings ) != AK_Success ) { assert( ! "Could not initialize the Sound Engine." ); return false; } (...) }
For more information about how to initialize the sound engine, refer to Advanced Sound Engine Integration
If your project uses the Interactive Music feature, the music engine must be initialized after the sound engine.
#include <AK/MusicEngine/Common/AkMusicEngine.h> // Music Engine (...) bool InitSoundEngine() { (...) // // Initialize the music engine // Using default initialization parameters // AkMusicSettings musicInit; AK::MusicEngine::GetDefaultInitSettings( musicInit ); if ( AK::MusicEngine::Init( &musicInit ) != AK_Success ) { assert( ! "Could not initialize the Music Engine." ); return false; } (...) }
If you want to be able to use the Wwise authoring application to connect to your game and perform in-game mixing, profiling and troubleshooting, the next step is to initialize communications. It is strongly recommended to do so, as in-game mixing, profiling and troubleshooting are extremely powerful ways for the sound designer to work on your game's audio in a more efficient way.
|
Note: To use communications, you need to link with the CommunicationCentral module. |
|
Note: Communications are available in the Debug and Profile configurations of the sound engine only. They are not needed in the retail version of your game, so they are not part of the Release build of the sound engine. The following code uses the AK_OPTIMIZED symbol to leave communication-specific code out of the release build. In this sample project, AK_OPTIMIZED is defined in Debug and Profile, but not in Release. |
// Include for communication between Wwise and the game -- Not needed in the release version #ifndef AK_OPTIMIZED #include <AK/Comm/AkCommunication.h> #endif // AK_OPTIMIZED (...) bool InitSoundEngine() { (...) #ifndef AK_OPTIMIZED // // Initialize communications (not in release build!) // AkCommSettings commSettings; AK::Comm::GetDefaultInitSettings( commSettings ); if ( AK::Comm::Init( commSettings ) != AK_Success ) { assert( ! "Could not initialize communication." ); return false; } #endif // AK_OPTIMIZED (...) }
You have now initialized all of the sound engine's modules. You should then register the plug-ins (see Plug-in Example). Refer to Create Recurring Calls to Perform Audio Processing for details on the calls you must make in your game loop to process the audio.
Some console communication libraries don't balance the initialization/termination calls properly; therefore, calling AK::Comm::Term will terminate the underlying low-level communication library for the console. This effectively closes all TCP/IP communication for the game. If your game needs to keep the communication libraries enabled after termination, AkCommSettings (used with Ak::Comm:Init) has a parameter to initialize the system library or not. If you choose to initialize the communication library yourself, check the code used by the Wwise sound engine below. The game should do something similar.
Windows sockets initialization:
WSAData wsaData = { 0 }; ::WSAStartup( MAKEWORD( 2, 2 ), &wsaData )
Termination:
::WSACleanup();
The AkCommSettings::ports member of the AkCommSettings structure represents network ports used for communication between the Wwise authoring application and the sound engine. All of these ports are opened in the game when Wwise communication is enabled.
One of the ports, AkCommSettings::Ports::uDiscoveryBroadcast, cannot be dynamic (cannot be set to 0). The authoring application needs to be aware of it in order to discover games on the network. Furthermore, the value specified here must be the same value specified in the Network tab of the Project Settings within the authoring application.
|
Tip: If your team is working on multiple games using Wwise, you might want to use different Discovery Broadcast ports for each game. That way when you open the Remote Connections window in the Wwise authoring application, only games corresponding to the currently opened Wwise project will be listed. When changing this port, be sure to change it both in the Project Settings within the authoring application and in the AkCommSettings structure you pass to AK::Comm::Init() in the game. |
The two other ports in the structure can be dynamic (or "ephemeral"). This means that instead of using a fixed port, one is automatically picked by the operating system:
These ports are dynamic by default. It is recommended to keep them dynamic in order to minimize chances of conflicts with other applications.
Wwise requires three sequential channels to works properly, and they must be ordered in the order they are presented in the Port structure. In Wwise, you can select the base port (By default 8).
If they conflict with ports used by other components of your game, simply change them in the AkCommSettings structure before calling AK::Comm::Init().
|
Note: In a multi-platform game, it is valid to use different ports on different platforms for AkCommSettings::Ports::uCommand and AkCommSettings::Ports::uNotification. |
|
Tip: For more information on dynamic/ephemeral ports, please consult these web sites: |
There is one more port involved in Wwise communications, but since it is opened in the authoring application only, it is not part of the AkCommSettings::Ports structure.
That port is used by the authoring application to receive reponses to the Discovery Broadcast messages sent to detect games running on the network. It is dynamic by default (set to 0) but can be changed in the Project Settings within the authoring application.