A Wwise plug-in DLL contains the following:
AK::Wwise::IAudioPlugin
interface.
|
Note: A DLL can contain more than one plug-in. For more information, refer to Exported Functions and Wwise Plug-in XML Description Files. |
It is recommended that you organize your project so that the sound engine effects are implemented in a static library linked within the plug-in DLL, and reused for linking in your game. For an example, check how the sample plug-ins provided with Wwise (Samples) have been organized.
You need to create a class derived from AK::Wwise::IAudioPlugin
that implements all of that interface's methods. This class will manage everything related to the UI and SoundBank generation for your plug-in.
#include <AK/Wwise/AudioPlugin.h> class SinePlugin : public AK::Wwise::IAudioPlugin { public: // All AK::Wwise::IAudioPlugin interface methods ... (...) // CompanyID and PluginID as defined in the XML file static const short CompanyID; static const short PluginID; private: AK::Wwise::IPluginPropertySet * m_pPSet; };
Implementing the functions of AK::Wwise::IAudioPlugin
defines the behavior of your plug-in in response various situations in Wwise. The functions allow the plug-in to react to the following situations:
Some of these topics will be covered in the following sections, but you can refer to the AK::Wwise::IAudioPlugin
reference for a complete description of the interface and the functions you need to implement.
When the Wwise user creates an instance of your source or effect plug-in, Wwise calls the AkCreatePlugin()
function in your DLL (Exported Functions) to create the new instance. When the user deletes an instance of your plug-in, Wwise calls the AK::Wwise::IPluginBase::Destroy()
method on that particular instance. This method must release any memory or other resources the object might consume, then deletes the object itself.
If the instance was created with the operator new
, a typical implementation of AK::Wwise::IPluginBase::Destroy()
would be:
void SinePlugin::Destroy() { delete this; }
An instance of AK::Wwise::IPluginPropertySet
is automatically created for each instance of your plug-in so you don't have to implement property support yourself. When an instance of your plug-in is created, AK::Wwise::IAudioPlugin::SetPluginPropertySet()
is called on your plug-in with a pointer to the AK::Wwise::IPluginPropertySet
instance.
You should code your plug-in object to keep this pointer as a member so you can query it when needed. For example, it may be queried in your implementation of AK::Wwise::IAudioPlugin::GetBankParameters()
. For more information, refer to Generating SoundBanks.
|
Note: The instance of AK::Wwise::IPluginPropertySet received by AK::Wwise::IAudioPlugin::SetPluginPropertySet() is guaranteed to exist until AK::Wwise::IPluginBase::Destroy() is called on your plug-in. |
If your plug-in uses sophisticated data, such as curves, graphs, and so on, instead of just simple properties, the property set won't be able to keep it. You will need to keep that in your plug-in class and use AK::Wwise::IPluginPropertySet::NotifyInternalDataChanged()
to inform Wwise that your particular data has changed. This will let Wwise know that your data needs to be saved or transferred to the sound engine. You should not define anything in the plug-in's XML for this type of data. When calling NotifyInternalDataChanged, you can specify a ParamID to limit the notification to a subset of your data. Use AK::IAkPluginParam::ALL_PLUGIN_DATA_ID
to specify that all your data has changed.
|
Note: Do not use NotifyInternalDataChanged for simple properties that are declared in the XML. The notification is done automatically when AK::Wwise::IPluginPropertySet::SetValue is used. |
When the user modifies a complex property and you call NotifyInternalDataChanged, Wwise will call you back through AK::Wwise::IAudioPlugin::GetPluginData
to obtain the data block that will be transferred to the sound engine part of your plug-in. Your sound engine plug-in will receive the block through AK::IAkPluginParam::SetParam
with the ParamID specified in NotifyInternalDataChanged.
|
Note: If you use complex properties, you must handle AK::IAkPluginParam::ALL_PLUGIN_DATA_ID in AK::IAkPluginParam::SetParam and AK::Wwise::IAudioPlugin::GetPluginData . It will be used at least once, when the plug-in is played the first time. |
Loading and saving the parameters of your plug-in is done automatically when your plug-in declares its properties in the XML. However, for complex data, you must provide your own persistence code. To do so, override AK::Wwise::IAudioPlugin::Save
and AK::Wwise::IAudioPlugin::Load
. See AK::IXmlTextReader
and AK::IXmlTextWriter
for more information. If your project is under source control, it is recommended that you save your data in XML to ease the merging of files. Also, the plug-in code is responsible for handling the versioning of its data. Make sure your plug-in knows what versions of the data it can or cannot load to avoid crashes and data corruption.
Wwise users can change property values either directly through a control in the dialog, or by using the undo/redo commands. When a user changes a property value, AK::Wwise::IAudioPlugin::NotifyPropertyChanged()
is called on your plug-in. This way, if your dialog is being displayed at that time, you can specify actions to be performed in the dialog such as enabling or disabling controls.
When Wwise users change the current platform using a list or a keyboard shortcut, instances of your plug-in are notified by a call to their AK::Wwise::IAudioPlugin::NotifyCurrentPlatformChanged()
method. This way, if your dialog is being displayed at that time, you can adapt it to the new current platform.
You must code your plug-in to store its current settings into banks when requested. You can do this by implementing the AK::Wwise::IAudioPlugin::GetBankParameters()
method.
The parameters are written as a data block into the SoundBank. The block is then loaded directly from the SoundBank into your sound engine plug-in's parameter structure. Therefore, you must write the parameters in the same order they are declared in the parameter structure of your sound engine plug-in. For more information, refer to Parameter Node Interface Implementation.
For example, consider the following parameter structure:
// Parameter structure for this effect. struct AkFXSrcSineParams { AkReal32 fFrequency; // Frequency (in Hertz). AkReal32 fGain; // Gain (in dBFS). AkReal32 fDuration; // Sustain duration (only valid if finite). };
The following implementation of AK::Wwise::IAudioPlugin::GetBankParameters()
gets the current value of each property and then uses the appropriate method to write the value with the AK::Wwise::IWriteData
object received as a parameter. These actions are performed in the same order in which the members are defined in your parameter structure.
bool SinePlugin::GetBankParameters( const GUID & in_guidPlatform, AK::Wwise::IWriteData* in_pDataWriter ) const { CComVariant varProp; m_pPSet->GetValue( in_guidPlatform, szSineFreq, varProp ); in_pDataWriter->WriteReal32( varProp.fltVal ); m_pPSet->GetValue( in_guidPlatform, szSineGain, varProp ); in_pDataWriter->WriteReal32( varProp.fltVal ); m_pPSet->GetValue( in_guidPlatform, szSineDuration, varProp ); in_pDataWriter->WriteReal32( varProp.fltVal ); return true; }
|
Note: To get the current value of a property, call AK::Wwise::IPluginPropertySet::GetValue() on the instance of AK::Wwise::IPluginPropertySet that was assigned to your plug-in through AK::Wwise::IAudioPlugin::SetPluginPropertySet() . |
For more information about available methods for writing different types of data, refer to AK::Wwise::IWriteData
.
Your DLL may need to contain resources to define the dialogs Wwise users access to edit the properties of your plug-in (Refer to Plug-in Dialog Resources for more information). You must implement the methods related to these dialogs in the AK::Wwise::IAudioPlugin
object.
The method AK::Wwise::IAudioPlugin::GetResourceHandle()
lets Wwise know where to look for your dialog resources. The following is a standard implementation using MFC:
// Get access to UI resource handle. HINSTANCE SinePlugin::GetResourceHandle() const { return AfxGetStaticModuleState()->m_hCurrentResourceHandle; }
Your plug-in object may also implement AK::Wwise::IAudioPlugin::GetDialog()
so Wwise can retrieve information about the dialog. This information includes the dialog's resource ID, and an optional AK::Wwise::PopulateTableItem
.
|
Note: Effect plug-ins have only one dialog, which is displayed in the Effect Settings tab of the Effect Editor. However, source plug-ins can be edited in two different places: the Contents Editor and the Source Plug-in Property Editor. Use the AK::Wwise::IAudioPlugin::eDialog parameter to return the appropriate information. |
Here is a typical implementation of AK::Wwise::IAudioPlugin::GetDialog()
for a source plug-in:
// Bind non static text UI controls to properties for property view AK_BEGIN_POPULATE_TABLE(ToneGenProp) AK_POP_ITEM(IDC_CHECK_SWEEPFREQ, szSweepFreq) AK_POP_ITEM(IDC_RADIO_FREQSWEEPLIN, szSweepFreqType) AK_POP_ITEM(IDC_RADIO_FIXLENGTH, szDurMode) AK_END_POPULATE_TABLE() // Determine what dialog has been called and set the property names to a UI control binding populated table. bool ToneGenPlugin::GetDialog( eDialog in_eDialog, UINT & out_uiDialogID, PopulateTableItem *& out_pTable ) const { CComVariant varProp; switch ( in_eDialog ) { case SettingsDialog: out_uiDialogID = IDD_TONEGENPLUGIN_BIG; out_pTable = ToneGenProp; return true; case ContentsEditorDialog: out_uiDialogID = IDD_TONEGENPLUGIN_SMALL; out_pTable = NULL; return true; } return false; }
And here is AK::Wwise::IAudioPlugin::GetDialog()
implemented for an Effect plug-in:
// Set the property names to a UI control binding populated table. bool DelayPlugin::GetDialog( eDialog in_eDialog, UINT & out_uiDialogID, PopulateTableItem *& out_pTable ) const { assert( in_eDialog == SettingsDialog ); out_uiDialogID = IDD_DELAY; out_pTable = NULL; return true; }
Finally, Effect and Audio Device plug-ins are allowed to not implement a new dialog. For these two plug-in types, if no dialog was provided, Wwise will create one dynamically based on the content of the XML plug-in definition file.
// If not providing dialog resources, Wwise will display a default dialog based on the content of the XML plug-in definition file. bool AkSinkPlugin::GetDialog( eDialog in_eDialog, UINT & out_uiDialogID, PopulateTableItem *& out_pTable ) const { // Returning false will only work for Effects and Audio Devices. // Other plug-in types will fail to display. return false; }
Refer to Plug-in Dialog Resources for details regarding the actual dialog resources, including format, controls, and so on.
Refer to How to Bind Regular Controls to Properties for information on when and how to use AK::Wwise::PopulateTableItem
and associated macros with your plug-in's dialogs.
If UI changes require specific actions, such as enabling or disabling controls, you can carry these out by interpreting the window messages received in the AK::Wwise::IAudioPlugin::WindowProc()
function:
// Standard window function allowing the user to intercept whatever message is of interest when implementing UI behavior. bool ToneGenPlugin::WindowProc( eDialog in_eDialog, HWND in_hWnd, UINT in_message, WPARAM in_wParam, LPARAM in_lParam, LRESULT & out_lResult ) { if ( in_message == WM_INITDIALOG ) { // Perform anything you need to do on dialog window initialization ... } // For example, catch window command actions (only for the main dialog) to enable/disable controls else if ( in_eDialog == SettingsDialog && in_message == WM_COMMAND ) { // Notification code switch ( HIWORD( in_wParam ) ) { case BN_CLICKED: // Check which button was clicked switch ( LOWORD( in_wParam ) ) { case IDC_CHECK_SWEEPFREQ: // Verify if check box was enabled if ( IsDlgButtonChecked( in_hWnd, IDC_CHECK_SWEEPFREQ ) == BST_CHECKED ) { // Enable some controls ... } else if ( IsDlgButtonChecked( in_hWnd, IDC_CHECK_SWEEPFREQ ) == BST_UNCHECKED ) { // Disable some controls ... } break; } } // End switch hi word (notification code) } // End command window event // Return False to let the parent window deal with the message. Return True // for messages you don't want the parent window to handle. return false; }
Implementing the AK::Wwise::IAudioPlugin::DisplayNameForProp()
function allows Wwise to retrieve a 'user-friendly' name for that property, which will be displayed in several places in the interface. These places include the RTPC Manager, in the edit menu for the undo/redo commands after a property change, and so on.
The in_szPropertyName
parameter corresponds to the property name specified in the plug-in's XML definition file (Wwise Plug-in XML Description Files). Here is a sample implementation of this method:
bool SinePlugin::DisplayNameForProp( LPCWSTR in_szPropertyName, LPWSTR out_szDisplayName, UINT in_unCharCount ) const { // Get resource handle HINSTANCE hInst = AfxGetStaticModuleState()->m_hCurrentResourceHandle; if ( ! wcscmp( in_szPropertyName, szSineFreq ) ) { ::LoadString( hInst, IDS_SINEFREQ, out_szDisplayName, in_unCharCount ); return true; } else if ( ! wcscmp( in_szPropertyName, szSineGain ) ) { ::LoadString( hInst, IDS_SINEGAIN, out_szDisplayName, in_unCharCount ); return true; } // ... return false; }
If a certain property has non-numeric values, such as a boolean property that means On/Off or Yes/No, or an enumeration for a curve type, or if some values have a special meaning, you can specify custom text to be displayed for some or all of the property's possible values in your implementation of the AK::Wwise::IAudioPlugin::DisplayNamesForPropValues()
method. The custom text will be used in the RTPC graph view for those values on the Y axis.
|
Note: The format of the string that is built by
For |
In the sample code below, the name is retrieved from the plug-in's resources:
// Allow Wwise to retrieve a user friendly name for that property's value (for example, RTPCs). bool DelayPlugin::DisplayNamesForPropValues( LPCWSTR in_szPropertyName, LPWSTR out_szValuesName, UINT in_unCharCount ) const { bool bFound = false; if ( !wcscmp( in_szPropertyName, szFeedbackEnabled ) ) { WCHAR szValueName[128]; CString csValuesName( L"0:" ); ::LoadString( AfxGetStaticModuleState()->m_hCurrentResourceHandle, IDS_PROPVALUENAME_FEEDBACKENABLED_OFF, szValueName, 128 ); csValuesName += szValueName; csValuesName += L",1:"; ::LoadString( AfxGetStaticModuleState()->m_hCurrentResourceHandle, IDS_PROPVALUENAME_FEEDBACKENABLED_ON, szValueName, 128 ); csValuesName += szValueName; _tcsncpy( out_szValuesName, csValuesName, in_unCharCount ); bFound = true; } else if ( !wcscmp( in_szPropertyName, szWetDryMix ) ) { WCHAR szValueName[128]; CString csValuesName( L"0:" ); ::LoadString( AfxGetStaticModuleState()->m_hCurrentResourceHandle, IDS_PROPVALUENAME_WETDRYMIX_DRY, szValueName, 128 ); csValuesName.AppendFormat( L"(%s) 0", szValueName ); csValuesName += L",100:"; ::LoadString( AfxGetStaticModuleState()->m_hCurrentResourceHandle, IDS_PROPVALUENAME_WETDRYMIX_WET, szValueName, 128 ); csValuesName.AppendFormat( L"(%s) 100", szValueName ); _tcsncpy( out_szValuesName, csValuesName, in_unCharCount ); bFound = true; } return bFound; }
When a Wwise user clicks on the '?' icon in a plug-in's dialog titlebar, AK::Wwise::IAudioPlugin::Help()
is called on that plug-in. You can implement Help with various tools, including HTMLHelp, WinHelp, or a third-party Help browser. The function receives a window handle that can be used as the parent for any window you want to display. The AK::Wwise::IAudioPlugin::eDialog
parameter allows you to choose a specific Help topic to match the dialog the Wwise user currently has open. This function must return true
if you handled the Help request, or false
otherwise, in which case Wwise will display a Help topic related to the Plug-in Manager.
|
Note: As previously mentioned, effect plug-ins have only one dialog, while source plug-ins have two. The AK::Wwise::IAudioPlugin::eDialog parameter should be used if your source plug-in has separate Help topics for its two dialogs. |
Here is an example of a source plug-in that uses HTML help:
// Implement online help when the user clicks on the "?" icon bool ToneGenPlugin::Help( HWND in_hWnd, eDialog in_eDialog ) const { AFX_MANAGE_STATE( ::AfxGetStaticModuleState() ) ; DWORD dwTopic = ONLINEHELP::Tone_Generator_Settings; if ( in_eDialog == AK::Wwise::IAudioPlugin::ContentsEditorDialog ) dwTopic = ONLINEHELP::Tone_Generator_ContentsEditor; // Note: Do NOT call AfxGetApp()->HtmlHelp() as it will launch the Help // window from the wrong parent window which will make floating views // behave unexpectedly. ::HtmlHelp( NULL, AfxGetApp()->m_pszHelpFilePath, HH_HELP_CONTEXT, dwTopic ); return true; }
|
Caution: The Help() method should NOT call AfxGetApp()->HtmlHelp(). It will launch the Help window from the wrong parent window which will make floating views behave unexpectedly. Instead, use HtmlHelp() with a NULL window handle, as shown in the example above. |
Your DLL must contain resources for your plug-in's dialog or dialogs. The resource ID for each dialog is the same ID returned by AK::Wwise::IAudioPlugin::GetDialog(). Refer to Dialog-Related Code for more information.
Refer to Wwise Plug-in Dialog Reference for a complete description of the controls you can use in your dialog, how to bind them to properties, and so on.
Each plug-in DLL must export two (or three) symbols:
AkCreatePlugin()
: Creates an instance of your AK::Wwise::IPluginBase
object.g_pAKPluginList:
Use the macro DEFINE_PLUGIN_REGISTER_HOOK
to define the symbol.AkGetSinkPluginDevices()
: Applicable only to Audio Device plug-ins (sink plug-in). Lists all available Audio Devices for this plug-in. See Wwise Authoring DLL for an audio device plug-in.The AkCreatePlugin()
function receives the Company ID and Plug-in ID as parameters. These IDs correspond to those defined in the plug-in XML definition file (Refer to Wwise Plug-in XML Description Files and Wwise Plug-in IDs for more information). If your DLL and XML file contain multiple plug-ins, these parameters will let you know which plug-in is being requested.
Here is an example of the exported function for a plug-in DLL containing one plug-in:
#include <AK/Wwise/Utilities.h> #include <AK/SoundEngine/Common/IAkPlugin.h> #include <assert.h> // Wwise UI plug-in factory AK::Wwise::IPluginBase* __stdcall AkCreatePlugin( unsigned short in_usCompanyID, unsigned short in_usPluginID ) { if ( in_usCompanyID == SinePlugin::CompanyID && in_usPluginID == SinePlugin::PluginID ) return new MyPlugin; // return AK::Wwise::IAudioPlugin-derived object in the case of a source plug-in. // If this function is called with the wrong company/plug-in ID, it is because // the IDs in the plug-in XML definition file and those in the code don't match. assert( false ); return NULL; } DEFINE_PLUGIN_REGISTER_HOOK AK_STATIC_LINK_PLUGIN(SineSource); //Static link the sound engine side of the plug-in.
In the library description file (.def) you should explicitly mention the two functions to be exported:
LIBRARY "MyPlugin"
EXPORTS
AkCreatePlugin
Wwise will only be able to use the plug-ins in the DLL if the plug-ins are properly registered into the sound engine. For this to happen, you must define a AK::PluginRegistration
class for your plug-in. This is code that should already be done for you in-game plug-in library (See Wwise Sound Engine Plug-ins Overview). For the authoring-side DLL, you also need to call AK::Wwise::RegisterWwisePlugin()
once, in the DllMain()
or InitInstance()
function of your DLL. Make sure your authoring side DLL code has one reference to AK_STATIC_LINK_PLUGIN(YourPlugin)
(this looks for the PluginRegistration
defined earlier).
The Wwise authoring tool can display a message box the first time your plug-in is added to a project, reminding your users of their legal obligations. You may embed this text within your plug-in, although this is not mandatory. Skip this section if you do not wish to use this feature.
In your plug-in's code, export the function AkGetFirstTimeCreationMessage(). Like the other exported functions, this function receives the Company ID and Plug-in ID as parameters (see Exported Functions above). This function also has two output arguments, the message string to display, and the string of the registry key that is used to know if the plug-in has already been created before.
Here is an example of an implementation of this function:
// Message to display when creating the plug-in for the first time. bool __stdcall AkGetFirstTimeCreationMessage( unsigned short in_usCompanyID, unsigned short in_usPluginID, BSTR& out_message, BSTR& out_key ) { assert( in_usCompanyID == SinePlugin::CompanyID && in_usPluginID == SinePlugin::PluginID ); AFX_MANAGE_STATE( AfxGetStaticModuleState() ); CString csText; csText.LoadString( IDS_SINEPLUGIN_LICENSEREMINDER ); out_message = csText.AllocSysString(); csText = _T("SinePlugin"); out_key = csText.AllocSysString(); return true; }
In this example, the license reminder text was stored in the plug-in's string table under IDS_SINEPLUGIN_LICENSEREMINDER.
The text "SinePlugin" that is returned via out_key is the name of the registry key that Wwise creates the first time this function is called, here: HKEY_CURRENT_USER/Software/Audiokinetic Inc./Wwise/PluginUserAcknowledge/Projects/{current project}/SinePlugin. Obviously, you will want to find a name that is unique to your plug-in and company. When Wwise calls this function again and finds this key in the registry, it will not display your message.
In the library description file (.def) add this function to the list of functions that need to be exported:
LIBRARY "MyPlugin"
EXPORTS
AkCreatePlugin
AkGetFirstTimeCreationMessage
An effect plug-in inside the master-mixer hierarchy (applied to a bus) can receive monitoring information from its associated run-time component in the sound engine. Refer to Posting Monitoring Data for Wwise Plug-ins for details on how to post monitoring data from the sound engine side of the plug-in.
When a monitoring data block is received by the plug-in instance (monitoring must be enabled in Wwise), the plug-in can parse the data properly and trigger actions like statistic compilations and or UI events (such as VU meters for instance). The code below show a simple example of how to react to monitoring data in the implementation of IAudioPlugin::NotifyMonitorData() in a way that handles byte swapping as necessary when the platform that posted the data has different endianness than that of the Wwise plug-in.
void MyPlugin::NotifyMonitorData( void * in_pData, unsigned int in_uDataSize, bool in_bNeedsByteSwap ) { // Parse the data block that was sent from your sound engine plug-in and // byte swap if platform's endianness is reported to be different than PC. unsigned int * pData = (unsigned int *) in_pData; unsigned int uNumChannels = in_bNeedsByteSwap ? _byteswap_ulong( *pData++ ) : *pData++; float fChannelPeaks[MAX_NUM_CHANNELS]; if ( in_bNeedsByteSwap ) { for ( unsigned int i = 0; i < uNumChannels; ++i ) { fChannelPeaks[i] = (float) _byteswap_ulong( *pData++ ); } } else { memcpy( fChannelPeaks, pData, uNumChannels*sizeof(float) ); } // Create some UI response to the incoming data... }
During SoundBank generation, Wwise queries the plug-ins for their license status. The plug-in can return one of the enumerated values in AK::Wwise::LicenseStatus. Additionally, the plug-in can also return a message with an associated severity, to be shown in the SoundBank generation log.
Returning LicenseStatus_Unlicensed or LicenseStatus_Expired will prevent the plug-in from being included in a SoundBank.
Plug-ins can implement their own schemes for license validation. Please note that you should never query a server synchronously in this function. The function needs to return an answer immediately.
If your plug-in is registered with us, you can leverage the license system of Wwise. Use the following implementation, which looks up the license status in the Wwise Project License:
AK::Wwise::LicenseStatus MyPlugin::GetLicenseStatus(const GUID & in_guidPlatform, AK::Wwise::Severity& out_eSeverity, LPWSTR out_pszMessage, unsigned int in_uiBufferSize) { AK::Wwise::LicenseType eType; AK::Wwise::LicenseStatus eStatus; UINT32 uDaysToExpiry; m_pPSet->GetLicenseStatus(in_guidPlatform, eType, eStatus, uDaysToExpiry); return eStatus; }
See Samples for a list of available sample plug-in projects you can look at for more information.
If you run into any problems, Help is available in the Wwise Source and Effect Plug-in Troubleshooting Guide.
Des questions ? Des problèmes ? Besoin de plus d'informations ? Contactez-nous, nous pouvons vous aider !
Visitez notre page d'AideEnregistrez votre projet et nous vous aiderons à démarrer sans aucune obligation !
Partir du bon pied avec Wwise