社区问答

欢迎来到 Audiokinetic 社区问答论坛。在此,Wwise 和 Strata 用户可互帮互助。如需我们团队直接提供协助,请前往技术支持申请单页面。若要报告问题,请在 Audiokinetic Launcher 中选择“报告错误”选项(注意,问答论坛并不会接收错误报告)。我们内部设有专门的错误报告系统,会有专人查看报告并设法解决问题。

要想尽快得到满意的解答,请在提问时注意以下几点:

  • 描述尽量具体:比如,想达到什么样的目的,或者具体哪里有问题。
  • 包含关键细节:比如,Wwise 和游戏引擎版本以及所用操作系统等等。
  • 阐明所做努力:阐明自己为了排除故障都采取了哪些措施。
  • 聚焦问题本身:聚焦于问题本身的相关技术细节,以便别人可以快速找到解决方案。

0 投票
Hi, have been looking around for a way to have different events play through different Output Devices (Speakers, and headphones for example) in Unreal Engine 4.

I can do this in the Wwise editor by going to Audio->Audio Preferences and then choose what bus will play on what output device.

This obviously only works in Wwise and not in UE4.

I have found this: https://www.audiokinetic.com/library/2017.1.9_6501/?source=SDK&id=namespace_a_k_1_1_sound_engine_a247218018cc62d203b9d36a55c5647af.html

However, this is for an older version and is not supported anymore it seems like?

There is also this: https://www.audiokinetic.com/library/edge/?source=SDK&id=namespace_a_k_1_1_sound_engine_a0ebeb52530b9871d9b66b5fa3bce192f.html

It seems like using this function allows you to tie Listeners to specific output devices. By default all Listeners route to the default output device. So I'm assuming it is a matter of creating at least two listeners, Calling this function, and append one of the listeners and hope for the best.

For future reference, I will keep this thread updated and add information when I make progress.

Any help would be appreciated!
分类:General Discussion | 用户: Dieter T (110 分)
修改于 用户:Dieter T
Did you ever figure this out? Having the exact same issues. The pure C++ SDK makes getting a device ID and adding it to a listener very easy, but things are far more limited in UE4's implementation. I'd want to have a simple way of setting this, but if not I've been exploring:

- Extending the functionality of AkAudio, which is the only way UE4's implementation supports Ak::SoundEngine calls (as warned here: https://www.audiokinetic.com/library/edge/?source=UE4&id=using_cpp.html).

- Bypassing the implementation entirely and treating my UE4 project as a custom C++ engine that uses the SDK
(here's a starting point: https://www.linkedin.com/pulse/integrating-wwise-custom-engine-i-matthew-rosen)

The latter is particularly complex and has all sorts of issues with UE4's source (for example, Wwise uses <windows.h>/<winnt.h> which have another definition of the TEXT macro UE4 uses). This all seems like a lot of trouble for something that should be simple, and is, outside of UE4.
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

1个回答

0 投票
用户: Ed K. (300 分)
...