123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- /*******************************************************************************
- The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
- Technology released in source code form as part of the game integration package.
- The content of this file may not be used without valid licenses to the
- AUDIOKINETIC Wwise Technology.
- Note that the use of the game engine is subject to the Unreal(R) Engine End User
- License Agreement at https://www.unrealengine.com/en-US/eula/unreal
-
- License Usage
-
- Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
- this file in accordance with the end user license agreement provided with the
- software or, alternatively, in accordance with the terms contained
- in a written agreement between you and Audiokinetic Inc.
- Copyright (c) 2023 Audiokinetic Inc.
- *******************************************************************************/
- #include "AkAudioInputManager.h"
- #include "AkAudioDevice.h"
- #include "AkAudioEvent.h"
- #if WITH_EDITOR
- #include "Editor.h"
- #endif
- #include "Wwise/API/WwiseSoundEngineAPI.h"
- #include "Misc/ScopeLock.h"
- #include <inttypes.h>
- #include "AkComponent.h"
- /*------------------------------------------------------------------------------------
- FAudioInputDelegates
- Helper struct that contains an audio samples delegate and an audio format delegate
- ------------------------------------------------------------------------------------*/
- struct FAudioInputDelegates
- {
- FAkGlobalAudioInputDelegate AudioSamplesDelegate;
- FAkGlobalAudioFormatDelegate AudioFormatDelegate;
- };
- /*------------------------------------------------------------------------------------
- FAkAudioInputHelpers
- ------------------------------------------------------------------------------------*/
- namespace FAkAudioInputHelpers
- {
- static FCriticalSection MapSection;
- static TArray<float*> AudioData = TArray<float*>();
- /* A Map of playing ids to input delegates */
- static TMap<uint32, FAudioInputDelegates> AudioInputDelegates = TMap<uint32, FAudioInputDelegates>();
- static AkSampleType* GetChannel(AkAudioBuffer* Buffer, AkUInt32 in_uIndex)
- {
- check(Buffer);
- check( in_uIndex < Buffer->NumChannels() );
- return (AkSampleType*)((AkUInt8*)(Buffer->GetInterleavedData()) + ( in_uIndex * sizeof(AkSampleType) * Buffer->MaxFrames() ));
- }
- static void UpdateDataPointers(AkAudioBuffer* BufferToFill)
- {
- AkUInt32 NumChannels = BufferToFill->NumChannels();
- for (AkUInt32 c = 0; c < NumChannels; ++c)
- {
- AudioData[c] = GetChannel(BufferToFill, c);
- }
- }
- // This is the equivalent of AkAudioBuffer::ZeroPadToMaxFrames, but doesn't use global variables.
- static void ZeroPadToMaxFrames(AkAudioBuffer* Buffer)
- {
- check(Buffer);
- auto pData = Buffer->GetInterleavedData();
- check(pData || Buffer->MaxFrames() == 0)
- // The following members MUST be copied locally due to multi-core calls to this function.
- const AkUInt32 uNumChannels = Buffer->NumChannels();
- const AkUInt32 uNumCurrentFrames = FMath::Min(Buffer->uValidFrames, Buffer->MaxFrames());
- const AkUInt32 uNumZeroFrames = Buffer->MaxFrames() - uNumCurrentFrames;
- if ( uNumZeroFrames )
- {
- check(pData);
- for ( AkUInt32 i = 0; i < uNumChannels; ++i )
- {
- FPlatformMemory::Memset( GetChannel(Buffer, i) + uNumCurrentFrames, 0, uNumZeroFrames * sizeof(AkSampleType) );
- }
- Buffer->uValidFrames = Buffer->MaxFrames();
- }
- }
- /* The global audio samples callback that searches AudioInputDelegates for
- the key PlayingID and executes the corresponding delegate*/
- static void GetAudioSamples(AkPlayingID PlayingID, AkAudioBuffer* BufferToFill)
- {
- if (!BufferToFill)
- {
- return;
- }
- BufferToFill->eState = AK_NoMoreData;
- AkUInt32 NumChannels = BufferToFill->NumChannels();
- const AkUInt16 NumFrames = BufferToFill->MaxFrames();
- BufferToFill->uValidFrames = NumFrames;
- FAkGlobalAudioInputDelegate SamplesCallback;
- {
- FScopeLock MapLock(&MapSection);
- auto Delegates = AudioInputDelegates.Find((uint32)PlayingID);
- if (Delegates)
- {
- SamplesCallback = Delegates->AudioSamplesDelegate;
- }
- }
-
- if (SamplesCallback.IsBound())
- {
- UpdateDataPointers(BufferToFill);
- if(auto AudioDataPtr = AudioData.GetData())
- {
- if (SamplesCallback.Execute((int)NumChannels, (int)NumFrames, AudioDataPtr))
- {
- BufferToFill->eState = AK_DataReady;
- }
- }
- }
- else
- {
- ZeroPadToMaxFrames(BufferToFill);
- }
- }
- /* The global audio format callback that searches AudioInputDelegates for
- the key PlayingID and executes the corresponding delegate*/
- static void GetAudioFormat(AkPlayingID PlayingID, AkAudioFormat& AudioFormat)
- {
- FAkGlobalAudioFormatDelegate FormatCallback;
- {
- FScopeLock MapLock(&MapSection);
- auto Delegates = AudioInputDelegates.Find((uint32)PlayingID);
- if (Delegates)
- {
- FormatCallback = Delegates->AudioFormatDelegate;
- }
- }
- if (FormatCallback.IsBound())
- {
- FormatCallback.Execute(AudioFormat);
- }
- const uint32 NumChannels = AudioFormat.channelConfig.uNumChannels;
- if (AudioData.Max() < (int32)NumChannels)
- {
- AudioData.Reserve(NumChannels);
- AudioData.AddUninitialized(AudioData.GetSlack());
- }
- }
- /**
- * Sets the main callbacks for the Wwise engine audio input plugin.
- *
- */
- static void SetAkAudioInputCallbacks()
- {
- auto* SoundEngine = IWwiseSoundEngineAPI::Get();
- if (UNLIKELY(!SoundEngine)) return;
- SoundEngine->AudioInputPlugin->SetAudioInputCallbacks(
- &FAkAudioInputHelpers::GetAudioSamples,
- &FAkAudioInputHelpers::GetAudioFormat,
- nullptr);
- }
- /* Protects against calling Wwise sound engine SetAudioInputCallbacks function more than once */
- static bool bIsInitialized = false;
- /* Calls the Wwise sound engine SetAudioInputCallbacks function*/
- static void TryInitialize()
- {
- if (!bIsInitialized)
- {
- SetAkAudioInputCallbacks();
- bIsInitialized = true;
- }
- #if WITH_EDITOR
- FEditorDelegates::EndPIE.AddLambda([](const bool bIsSimulating)
- {
- bIsInitialized = false;
- });
- #endif
- }
- static void AddAudioInputPlayingID(AkPlayingID PlayingID,
- FAkGlobalAudioInputDelegate AudioSamplesDelegate,
- FAkGlobalAudioFormatDelegate AudioFormatDelegate)
- {
- FScopeLock MapLock(&MapSection);
- AudioInputDelegates.Add((uint32)PlayingID, { AudioSamplesDelegate, AudioFormatDelegate });
- }
- /* Posts an event and associates the AudioSamplesDelegate and AudioFormatDelegate delegates with the resulting playing id. */
- AkPlayingID PostAudioInputEvent(TFunction<AkPlayingID(FAkAudioDevice* AkDevice)> PostEventCall,
- FAkGlobalAudioInputDelegate AudioSamplesDelegate,
- FAkGlobalAudioFormatDelegate AudioFormatDelegate)
- {
- TryInitialize();
- AkPlayingID PlayingID = AK_INVALID_PLAYING_ID;
- FAkAudioDevice* AkDevice = FAkAudioDevice::Get();
- if (AkDevice != nullptr)
- {
- PlayingID = PostEventCall(AkDevice);
- if (PlayingID != AK_INVALID_PLAYING_ID)
- {
- AddAudioInputPlayingID(PlayingID, AudioSamplesDelegate, AudioFormatDelegate);
- }
- }
- return PlayingID;
- }
- static void EventCallback(AkCallbackType CallbackType, AkCallbackInfo *CallbackInfo)
- {
- if (CallbackType == AkCallbackType::AK_EndOfEvent)
- {
- AkEventCallbackInfo* EventInfo = (AkEventCallbackInfo*)CallbackInfo;
- if (EventInfo != nullptr)
- {
- uint32 PlayingID = (uint32)EventInfo->playingID;
- {
- FScopeLock MapLock(&MapSection);
- AudioInputDelegates.Remove(PlayingID);
- }
- }
- }
- }
- }
- /*------------------------------------------------------------------------------------
- FAkAudioInputManager
- ------------------------------------------------------------------------------------*/
- AkPlayingID FAkAudioInputManager::PostAudioInputEvent(
- UAkAudioEvent * Event,
- AActor * Actor,
- FAkGlobalAudioInputDelegate AudioSamplesDelegate,
- FAkGlobalAudioFormatDelegate AudioFormatDelegate,
- EAkAudioContext AudioContext
- )
- {
- if (!IsValid(Event))
- {
- UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioInputManager::PostAudioInputEvent: Invalid AkEvent."))
- return AK_INVALID_PLAYING_ID;
- }
- if (!IsValid(Actor))
- {
- UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioInputManager::PostAudioInputEvent: Invalid Actor playing AkEvent %s."), *Event->GetName())
- return AK_INVALID_PLAYING_ID;
- }
- return FAkAudioInputHelpers::PostAudioInputEvent([Event, Actor, AudioContext](FAkAudioDevice* AkDevice)
- {
- const auto Result = Event->PostOnActor(
- Actor,
- nullptr,
- &FAkAudioInputHelpers::EventCallback,
- nullptr,
- AkCallbackType::AK_EndOfEvent,
- nullptr,
- false,
- AudioContext);
- UE_CLOG(UNLIKELY(Result == AK_INVALID_PLAYING_ID), LogAkAudio, Warning,
- TEXT("FAkAudioInputManager::PostAudioInputEvent: Failed posting input event %s to actor %s."), *Event->GetName(), *Actor->GetName());
- UE_CLOG(LIKELY(Result != AK_INVALID_PLAYING_ID), LogAkAudio, VeryVerbose,
- TEXT("FAkAudioInputManager::PostAudioInputEvent: Posted input event %s to actor %s. PlayId=%" PRIu32), *Event->GetName(), *Actor->GetName(), Result);
- return Result;
- }, AudioSamplesDelegate, AudioFormatDelegate);
- }
- AkPlayingID FAkAudioInputManager::PostAudioInputEvent(
- UAkAudioEvent* Event,
- UAkComponent* Component,
- FAkGlobalAudioInputDelegate AudioSamplesDelegate,
- FAkGlobalAudioFormatDelegate AudioFormatDelegate,
- EAkAudioContext AudioContext)
- {
- if (!IsValid(Event))
- {
- UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioInputManager::PostAudioInputEvent: Invalid AkEvent."))
- return AK_INVALID_PLAYING_ID;
- }
- if (!Component)
- {
- UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioInputManager::PostAudioInputEvent: Invalid Component playing AkEvent %s."), *Event->GetName())
- return AK_INVALID_PLAYING_ID;
- }
- return FAkAudioInputHelpers::PostAudioInputEvent([Event, Component, AudioContext](FAkAudioDevice* AkDevice)
- {
- const auto Result = Event->PostOnComponent(
- Component,
- nullptr,
- &FAkAudioInputHelpers::EventCallback,
- nullptr,
- AkCallbackType::AK_EndOfEvent,
- nullptr,
- false,
- AudioContext);
- UE_CLOG(UNLIKELY(Result == AK_INVALID_PLAYING_ID), LogAkAudio, Warning,
- TEXT("FAkAudioInputManager::PostAudioInputEvent: Failed posting input event %s to component %s."), *Event->GetName(), *Component->GetName());
- UE_CLOG(LIKELY(Result != AK_INVALID_PLAYING_ID), LogAkAudio, VeryVerbose,
- TEXT("FAkAudioInputManager::PostAudioInputEvent: Posted input event %s to component %s. PlayId=%" PRIu32), *Event->GetName(), *Component->GetName(), Result);
- return Result;
- }, AudioSamplesDelegate, AudioFormatDelegate);
- }
- AkPlayingID FAkAudioInputManager::PostAudioInputEvent(
- UAkAudioEvent* Event,
- AkGameObjectID GameObject,
- FAkGlobalAudioInputDelegate AudioSamplesDelegate,
- FAkGlobalAudioFormatDelegate AudioFormatDelegate,
- EAkAudioContext AudioContext)
- {
- if (!IsValid(Event))
- {
- UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioInputManager::PostAudioInputEvent: Invalid AkEvent."))
- return AK_INVALID_PLAYING_ID;
- }
- if (GameObject == AK_INVALID_GAME_OBJECT)
- {
- UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioInputManager::PostAudioInputEvent: Invalid GameObject playing AkEvent %s."), *Event->GetName())
- return AK_INVALID_PLAYING_ID;
- }
- return FAkAudioInputHelpers::PostAudioInputEvent([Event, GameObject, AudioContext](FAkAudioDevice* AkDevice)
- {
- const auto Result = Event->PostOnGameObjectID(
- GameObject,
- nullptr,
- &FAkAudioInputHelpers::EventCallback,
- nullptr,
- AkCallbackType::AK_EndOfEvent,
- nullptr,
- AudioContext);
- UE_CLOG(UNLIKELY(Result == AK_INVALID_PLAYING_ID), LogAkAudio, Warning,
- TEXT("FAkAudioInputManager::PostAudioInputEvent: Failed posting input event %s to %" PRIu64 "."), *Event->GetName(), GameObject);
- UE_CLOG(LIKELY(Result != AK_INVALID_PLAYING_ID), LogAkAudio, VeryVerbose,
- TEXT("FAkAudioInputManager::PostAudioInputEvent: Posted input event %s to %" PRIu64 ". PlayId=%" PRIu32), *Event->GetName(), GameObject, Result);
- return Result;
- }, AudioSamplesDelegate, AudioFormatDelegate);
- }
- AkPlayingID FAkAudioInputManager::PostAudioInputEvent(UAkAudioEvent* Event,
- FAkGlobalAudioInputDelegate AudioSamplesDelegate, FAkGlobalAudioFormatDelegate AudioFormatDelegate,
- EAkAudioContext AudioContext)
- {
- if (!IsValid(Event))
- {
- UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioInputManager::PostAudioInputEvent: Invalid AkEvent."))
- return AK_INVALID_PLAYING_ID;
- }
- return FAkAudioInputHelpers::PostAudioInputEvent([Event, AudioContext](FAkAudioDevice* AkDevice)
- {
- const auto Result = Event->PostAmbient(
- nullptr,
- &FAkAudioInputHelpers::EventCallback,
- nullptr,
- AkCallbackType::AK_EndOfEvent,
- nullptr,
- AudioContext);
- UE_CLOG(UNLIKELY(Result == AK_INVALID_PLAYING_ID), LogAkAudio, Warning,
- TEXT("FAkAudioInputManager::PostAudioInputEvent: Failed posting ambient input event %s."), *Event->GetName());
- UE_CLOG(LIKELY(Result != AK_INVALID_PLAYING_ID), LogAkAudio, VeryVerbose,
- TEXT("FAkAudioInputManager::PostAudioInputEvent: Posted ambient input event %s. PlayId=%" PRIu32), *Event->GetName(), Result);
- return Result;
- }, AudioSamplesDelegate, AudioFormatDelegate);
- }
|