Hi, yes I did solve this with some help from the support!
Unfortunately, since the DLLs for Wwise is loaded in the plugin, normal behaviour, this means you have to make these changes directly in the Wwise plugin, i.e branch out from AudioKinetic´s version.
So what I did was: In the AKSettings.h I added a TMap that will store the OutputDevice->Device Shareset (same configuration that Wwise authoring tool uses).
/*
* Add the name of the output device together with the associated DeviceShareset.
* Key = HardwareOutputDevice
* Value = Device Shareset
*/
UPROPERTY(Config, EditAnywhere, Category = "OutputDevice")
TMap<FString, FString> OutputDevices;
In AKAudioDevice.h add this variable:
//Array of Output Device IDs that has been created from AK::SoundEngine::AddOutput
TArray<AkOutputDeviceID> OutputDeviceIDs;
This is to keep track of all the different outputs you've added to the AKSettings.h TMap.
Then in AKAudioDevice.cpp, change the function OnActorSpawned to look like this:
void FAkAudioDevice::OnActorSpawned(AActor* SpawnedActor)
{
APlayerCameraManager* AsPlayerCameraManager = Cast<APlayerCameraManager>(SpawnedActor);
if (AsPlayerCameraManager && AsPlayerCameraManager->GetWorld()->AllowAudioPlayback())
{
APlayerController* CameraOwner = Cast<APlayerController>(AsPlayerCameraManager->GetOwner());
if (CameraOwner && CameraOwner->IsLocalPlayerController())
{
FScopeLock Lock(&AkSettingsSection);
const UAkSettings* AkSettings = GetDefault<UAkSettings>();
if (AkSettings)
{
if (AkSettings->OutputDevices.Num() <= 0)
{
UAkComponent* pAkComponent = NewObject<UAkComponent>(SpawnedActor);
if (pAkComponent != nullptr)
{
pAkComponent->RegisterComponentWithWorld(SpawnedActor->GetWorld());
pAkComponent->AttachToComponent(SpawnedActor->GetRootComponent(), FAttachmentTransformRules::KeepWorldTransform, FName());
AddDefaultListener(pAkComponent);
}
}
else
{
for (auto& Elem : AkSettings->OutputDevices)
{
const char* HardwareOutputDevice = TCHAR_TO_ANSI(*Elem.Key);
UAkComponent * pAkComponent1 = NewObject<UAkComponent>(SpawnedActor);
if (pAkComponent1 != nullptr)
{
pAkComponent1->RegisterComponentWithWorld(SpawnedActor->GetWorld());
pAkComponent1->AttachToComponent(SpawnedActor->GetRootComponent(), FAttachmentTransformRules::KeepWorldTransform, FName());
AddDefaultListener(pAkComponent1);
}
AkGameObjectID listenerID1 = pAkComponent1->GetAkGameObjectID();
AkUInt32 uDeviceID1 = AK::GetDeviceIDFromName(const_cast<TCHAR*>(*Elem.Key));
// Add a secondary output connected to the device found previously, associated with listener1.
const char* DeviceShareset = TCHAR_TO_ANSI(*Elem.Value);
AkOutputSettings outputSettings1(DeviceShareset, uDeviceID1);
AkOutputDeviceID outID;
AKRESULT res1 = AK::SoundEngine::AddOutput(outputSettings1, &outID, &listenerID1, 1);
if (res1 != AK_Success)
{
UE_LOG(LogAkAudio, Error, TEXT("Failed to create output %d"), res1);
}
else
{
OutputDeviceIDs.Add(outID);
}
}
}
}
}
}
}
And change the Init function in AKAudioDevice.cpp so that it has these two lambda functions:
FEditorDelegates::EndPIE.AddLambda(
[&](const bool bIsSimulating)
{
if (!bIsSimulating)
{
AddDefaultListener(EditorListener);
// The Editor Listener should NEVER be the spatial audio listener
if (m_SpatialAudioListener == EditorListener)
{
AK::SpatialAudio::UnregisterListener(m_SpatialAudioListener->GetAkGameObjectID());
m_SpatialAudioListener = nullptr;
}
FScopeLock Lock(&AkSettingsSection);
const UAkSettings* AkSettings = GetDefault<UAkSettings>();
if (AkSettings)
{
for (AkOutputDeviceID ID : OutputDeviceIDs)
{
AKRESULT result = AK::SoundEngine::RemoveOutput(ID);
if (result != AK_Success)
{
UE_LOG(LogAkAudio, Error, TEXT("Failed to remove output %d"), result);
}
}
}
}
}
);
FWorldDelegates::LevelRemovedFromWorld.AddLambda(
[&](ULevel* LevelOpened, UWorld* bAsTemplate)
{
FScopeLock Lock(&AkSettingsSection);
const UAkSettings* AkSettings = GetDefault<UAkSettings>();
if (AkSettings)
{
for (AkOutputDeviceID ID : OutputDeviceIDs)
{
AKRESULT result = AK::SoundEngine::RemoveOutput(ID);
if (result != AK_Success)
{
UE_LOG(LogAkAudio, Error, TEXT("Failed to remove output %d"), result);
}
}
}
}
);
This because OnActorSpawn is called when a new level is loaded, so before that happens we want to reset/remove all outputs, otherwise the sound engine will not work!
Hope this helped!
/Linus