Skip to content
Snippets Groups Projects
Commit 97513198 authored by Jonathan Ehret's avatar Jonathan Ehret
Browse files

Merge remote-tracking branch 'origin/HEAD' into dev/5.1

parents 2130fffa 4f5e85db
No related branches found
No related tags found
No related merge requests found
Showing
with 463 additions and 110 deletions
......@@ -2,24 +2,28 @@
This Plugin allows the user to use the [VA Server](http://www.virtualacoustics.org/) from the [Institute of Acoustics](https://www.akustik.rwth-aachen.de/cms/~dwma/Technische-Akustik/lidx/1/) (ITA) of the RWTH Aachen to playback sounds in Unreal.
# Installation
To install the Plugin, clone the repository into the "Plugins" folder of your Unreal Engine 4 Project. Moreover make sure to have the VA Server prepared.
## Installation
If you used the [RWTH VR Project Template](https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unrealprojecttemplate) to create your project you can use the setup script to add the Virtual Acoustics Plugin.
Otherwise, add repository as submodule in the "Plugins" folder of your Unreal Engine 4 Project (``git submodule add https://git-ce.rwth-aachen.de/vr-vis/VR-Group/unreal-development/plugins/unreal-va-plugin.git Plugins/VAPlugin``.
The Server can either be used in its original form ([Link](http://www.virtualacoustics.org/)) or with the especially for this Plugin optimized Python Connection script ([Link](https://devhub.vr.rwth-aachen.de/VR-Group/vaserver), Recommended).
Using the Version with the Python script has the adventage of not needing to Restart the Server all the time manually and make sure to follow the instructions which are currently in its Readme file.
If you are using the automatic VAServer launching either have VAServer cloned out next to the folder of your project or specify the VALauncher Path in the Engine/Virtual Acoustics(VA) section of the project settings.
Moreover make sure to have the VAServer prepared:
The Plugin requires to have the [nDisplayExtensions](https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/ndisplayextensions) installed.
While the VAServer can be used in its original form ([Link](http://www.virtualacoustics.org/)) we highly recommend using the [VAServerLauncher](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/vaserverlauncher) script (Recommended), an especially for this Plugin optimized Python script including a lot of config possibilities and providing VAServer binaries.
Using the [VAServerLauncher](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/vaserverlauncher) has following the advantages:
* You don't need to (re)start the VAServer all the time manually. Even the VAServerLauncher script is (if configured correctly, see below) started automatically by Unreal.
* The VAServerLauncher can transfer used audio files from you Unreal project to the VAServer (even in networked environments), so you don't have to do this manually. Just use audio paths relative to your Content folder. (:warning: when using packaged builds, e.g., in the CAVE, make sure to have the paths where your audio files are packed as nonUFS, so not added to the pak files, this can be configured in the project settings at ``DirectoriesToAlwaysStageAsNonUFS``).
* This copying over is also true for VAServer ini files (e.g., having different acoustical renderers in different scenes), see [VAServerLAuncher documentation](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/VAServerLauncher/-/blob/master/README.md) for more information.
If you are using the automatic VAServerLauncher either have VAServerLauncher cloned next to the folder of your project or specify the VALauncher Path in the Engine/Virtual Acoustics(VA) section of the project settings.
Make sure to follow the instructions which are currently in [its Readme file](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/VAServerLauncher/-/blob/master/README.md).
The Plugin requires to have the [RWTH VR Toolkit](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/unreal-development/plugins/rwth-vr-toolkit) used in your project.
## Usage
For a more detailed C++ / Blueprint usage and a Documentation of the Plugins public functions, please check out the matching wiki page for each public Class and Enum:
* [VAReceiverActor](https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unreal-va-plugin/-/wikis/Documentation/VAReceiverActor)
For a more detailed C++ / Blueprint usage and a Documentation of the Plugins public functions, please check out the matching [wiki page](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/unreal-development/plugins/unreal-va-plugin/-/wikis/home) for each public class:
* [VAReceiverActor](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/unreal-development/plugins/unreal-va-plugin/-/wikis/Documentation/VAReceiverActor)
* Actor handling the Connection and Scene Settings for the current world, as well as the Position updates for the Receiver. If there is no Receiver Actor placed in the Scene, there will be created a new one with Default values at Runtime.
* [VAReflectionWall](https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unreal-va-plugin/-/wikis/Documentation/VAReflectionWall)
* Actor representing a wall on which the Sound Sources should reflect on. Make sure that its shape alligns with the wall. Please make sure to use symmetrical Directivities for the Sound Sources due to the fact that the directivities cannot be mirrored among the wall.
* [VASoundSourceComponent](https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unreal-va-plugin/-/wikis/Documentation/VASoundSourceComponent)
* Actor Component representing a Sound Source. Has to be attatched to something. It can have a graphical representation and reflecitons, which can be created with the Reflection Walls
* [VAEnums](https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unreal-va-plugin/-/wikis/Documentation/VAEnmus)
* All Enums used within the Plugin. At current state, only the EPlayAction is used for the Interface functions
\ No newline at end of file
* [VASoundSourceComponent](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/unreal-development/plugins/unreal-va-plugin/-/wikis/Documentation/VASoundSourceComponent)
* Actor Component representing a Sound Source. Has to be attatched to something. It can have a graphical representation and reflections, which can be created with the [VAReflectionWall](https://git-ce.rwth-aachen.de/vr-vis/VR-Group/unreal-development/plugins/unreal-va-plugin/-/wikis/Documentation/VAReflectionWall)
// Fill out your copyright notice in the Description page of Project Settings.
#include "SignalSources/VAAudioInputSignalSource.h"
#include "VAUtils.h"
#include "VAPlugin.h"
void UVAAudioInputSignalSource::Initialize()
{
if (bInitialized)
{
FVAUtils::LogStuff("[UVAAudioInputSignalSource::Initialize()]: Signal source is already initialized, aborting...", true);
return;
}
ID = FVAPlugin::GetAudioInputSignalSourceID(Channel);
if (!IsValidID(ID))
{
FVAUtils::LogStuff("[UVAAudioInputSignalSource::Initialize()]: Error initializing Audio Input Signal Source", true);
return;
}
bInitialized = true;
}
......@@ -13,6 +13,7 @@
void UVAAudiofileSignalSource::Initialize()
{
StorePlayStateInternallyEvent.Attach(this);
if (bInitialized)
{
FVAUtils::LogStuff("[UVAAudiofileSignalSource::Initialize()]: Signal source is already initialized, aborting...", false);
......@@ -27,6 +28,15 @@ void UVAAudiofileSignalSource::Initialize()
bInitialized = true;
}
UVAAudiofileSignalSource::~UVAAudiofileSignalSource()
{
if(bInitialized)
{
StorePlayStateInternallyEvent.Detach();
//otherwise it was never attached and would throw a warning
}
}
// ****************************************************************** //
// ******* Bluepring Functions ************************************** //
// ****************************************************************** //
......@@ -111,6 +121,7 @@ bool UVAAudiofileSignalSource::SetLoop(const bool bLoopN)
return true;
}
bLoop = bLoopN;
if (!UVirtualRealityUtilities::IsMaster())
{
......@@ -122,7 +133,6 @@ bool UVAAudiofileSignalSource::SetLoop(const bool bLoopN)
return true;
}
bLoop = bLoopN;
return FVAPlugin::SetSignalSourceBufferLooping(ID, bLoop);
}
......@@ -137,15 +147,18 @@ bool UVAAudiofileSignalSource::SetPlayBackPosition(const float Time)
bool UVAAudiofileSignalSource::SetPlayAction(const int Action)
{
if (!UVirtualRealityUtilities::IsMaster())
if (!bInitialized)
{
return false;
}
InterallyStoredPlayAction = Action;
StorePlayStateInternallyEvent.Send(Action); //also send this to all slaves, so potentially still pending send numbers from GetPlayAction are overwritten
if (int(GetPlayAction()) == Action)
if (!UVirtualRealityUtilities::IsMaster())
{
return true;
return false;
}
return FVAPlugin::SetSignalSourceBufferPlayAction(ID, EPlayAction::Type(Action));
}
......@@ -165,18 +178,33 @@ bool UVAAudiofileSignalSource::GetLoop() const
return bLoop;
}
EPlayAction::Type UVAAudiofileSignalSource::GetPlayActionEnum() const
EPlayAction::Type UVAAudiofileSignalSource::GetPlayActionEnum(bool bDirectOnMaster /*= false*/)
{
return EPlayAction::Type(GetPlayAction());
return EPlayAction::Type(GetPlayAction(bDirectOnMaster));
}
int UVAAudiofileSignalSource::GetPlayAction() const
int UVAAudiofileSignalSource::GetPlayAction(bool bDirectOnMaster /*= false*/)
{
if (!UVirtualRealityUtilities::IsMaster())
if(!bInitialized)
{
return -1;
}
return FVAPlugin::GetSignalSourceBufferPlayAction(ID);
//we return the internally stored action in case this is in cluster and not the master
//but also update the internally stored data by the one which the master can get from the VAServer
//However, using cluster events to sync, so new data might only be available next frame!
if (UVirtualRealityUtilities::IsMaster())
{
// in case of not being in cluster mode this directly calls StorePlayStateInternally() updating InterallyStoredPlayAction directly
// otherwise (in cluster mode) this emits acluster event, so the internal data will be updated before next Tick
int VAServerPlayAction = FVAPlugin::GetSignalSourceBufferPlayAction(ID);
StorePlayStateInternallyEvent.Send(VAServerPlayAction);
if(bDirectOnMaster)
{
return VAServerPlayAction;
}
}
return InterallyStoredPlayAction;
}
bool UVAAudiofileSignalSource::CopySignalSourceSettings(const std::string& OtherID)
......@@ -202,3 +230,8 @@ bool UVAAudiofileSignalSource::CopySignalSourceSettings(const std::string& Other
}
return FVAPlugin::SetSignalSourceBufferPlayAction(OtherID, EPlayAction::Type(PlayAction));
}
void UVAAudiofileSignalSource::StorePlayStateInternally(int PlayAction)
{
InterallyStoredPlayAction = PlayAction;
}
......@@ -53,14 +53,18 @@ void UVAAbstractSourceComponent::TickComponent(const float DeltaTime, const ELev
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!FVAPlugin::GetUseVA() || !UVirtualRealityUtilities::IsMaster())
if (!FVAPlugin::GetUseVA() || !SoundSource.IsValid())
{
return;
}
if (!bInitialized)
{
if(!bInformedNotInitialized)
{
FVAUtils::OpenMessageBox("[UVASourceComponent::TickComponent()]: Sound source is not initialized", true);
bInformedNotInitialized = true;
}
return;
}
......@@ -83,6 +87,38 @@ void UVAAbstractSourceComponent::TickComponent(const float DeltaTime, const ELev
}
void UVAAbstractSourceComponent::UpdateSkeletalMeshIfAttachToBone()
{
if (MovementSetting == EMovement::AttachToBone)
{
AActor* Owner = GetOwner();
bool bFoundSkelMesh = false;
while(!bFoundSkelMesh && Owner != nullptr)
{
TArray<UActorComponent*> MeshComponents = Owner->GetComponentsByClass(USkeletalMeshComponent::StaticClass());
for (UActorComponent* Component : MeshComponents)
{
USkeletalMeshComponent* MeshComp = Cast<USkeletalMeshComponent>(Component);
if (MeshComp && MeshComp->DoesSocketExist(FName(*BoneName)))
{
//found the right mesh component
SkeletalMeshComponent = MeshComp;
bFoundSkelMesh = true;
break;
}
}
Owner = Owner->GetAttachParentActor();
}
if (SkeletalMeshComponent == nullptr)
{
FVAUtils::OpenMessageBox("[UVASourceComponent::Initialize()]: Could not find bone " +
BoneName + ", using MoveWithObject instead.", true);
MovementSetting = EMovement::MoveWithObject;
}
}
}
void UVAAbstractSourceComponent::Initialize()
{
if (!FVAPlugin::GetUseVA() || bInitialized)
......@@ -126,25 +162,7 @@ void UVAAbstractSourceComponent::Initialize()
UpdateRate = ReceiverActorTmp->GetUpdateRate();
if (MovementSetting == EMovement::AttachToBone)
{
TArray<UActorComponent*> MeshComponents = GetOwner()->GetComponentsByClass(USkeletalMeshComponent::StaticClass());
for(UActorComponent* Component : MeshComponents)
{
USkeletalMeshComponent* MeshComp = Cast<USkeletalMeshComponent>(Component);
if(MeshComp && MeshComp->DoesSocketExist(FName(*BoneName)))
{
//found the right mesh component
SkeletalMeshComponent = MeshComp;
}
}
if (SkeletalMeshComponent == nullptr)
{
FVAUtils::OpenMessageBox("[UVASourceComponent::Initialize()]: Could not find bone " +
BoneName + ", using MoveWithObject instead.", true);
MovementSetting = EMovement::MoveWithObject;
}
}
UpdateSkeletalMeshIfAttachToBone();
SpawnPosition = GetOwner()->GetTransform().GetLocation();
......@@ -371,6 +389,7 @@ bool UVAAbstractSourceComponent::SetMovementSetting(const EMovement::Type NewMov
}
MovementSetting = NewMovementSetting;
UpdateSkeletalMeshIfAttachToBone();
UpdatePose();
return true;
......@@ -468,6 +487,24 @@ bool UVAAbstractSourceComponent::SetDirectivityByFileName(const FString FileName
return SoundSource->SetDirectivity(CurrentReceiverActor->GetDirectivityByFileName(FileName));
}
bool UVAAbstractSourceComponent::SetDefaultDirectivity()
{
if (!ShouldSendCommand())
{
return false;
}
return SoundSource->SetDirectivity(FVADirectivityManager::GetDefaultDirectivity());
}
bool UVAAbstractSourceComponent::SetNoDirectivity()
{
if (!ShouldSendCommand())
{
return false;
}
return SoundSource->RemoveDirectivity();
}
FString UVAAbstractSourceComponent::GetDirectivityFileName() const
{
if (SoundSource.IsValid())
......
// Fill out your copyright notice in the Description page of Project Settings.
#include "SoundSource/VAAudioInputSourceComponent.h"
#include "SignalSources/VAAudioInputSignalSource.h"
UVAAudioInputSourceComponent::UVAAudioInputSourceComponent() : Super()
{
SignalSource = CreateDefaultSubobject<UVAAudioInputSignalSource>("AudioInputSignalSource");
}
......@@ -8,6 +8,7 @@
#include "SoundSource/VASoundSourceRepresentation.h"
#include "Engine/World.h"
#include "Utility/VirtualRealityUtilities.h"
// ****************************************************************** //
......@@ -73,7 +74,7 @@ void FVASoundSource::SetPosition(const FVector NewPosition)
{
Position = NewPosition;
if (!FVAPlugin::SetSoundSourcePosition(SoundSourceID, Position))
if (!FVAPlugin::SetSoundSourcePosition(SoundSourceID, Position) && UVirtualRealityUtilities::IsMaster())
{
FVAUtils::LogStuff("[FVASoundSource::SetPosition()]:" +
FString(" Could not set sound source position in VA. Position of visual and auditive representation might mismatch now."), true);
......@@ -86,7 +87,7 @@ void FVASoundSource::SetRotation(const FRotator NewRotation)
{
Rotation = NewRotation;
if (!FVAPlugin::SetSoundSourceRotation(SoundSourceID, Rotation))
if (!FVAPlugin::SetSoundSourceRotation(SoundSourceID, Rotation) && UVirtualRealityUtilities::IsMaster())
{
FVAUtils::LogStuff("[FVASoundSource::SetRotation()]:" +
FString(" Could not set sound source rotation in VA. Orientation of visual and auditive representation might mismatch now."), true);
......
......@@ -409,6 +409,9 @@ bool FVAPlugin::DisconnectServer()
void FVAPlugin::AddVAServerSearchPath(const std::string& SearchPath)
{
if (!ShouldInteractWithServer())
return;
// This checking whether it already exists, led to undeterministic runtime error, due to std::string dtor.
// So keep track whether it was added before yourself ;-)
/*CVAStruct Searchpaths = VAServer->GetSearchPaths();
......@@ -557,6 +560,25 @@ bool FVAPlugin::SetSignalSourceBufferLooping(const std::string& SignalSourceID,
}
}
std::string FVAPlugin::GetAudioInputSignalSourceID(const int Channel)
{
if (!ShouldInteractWithServer())
return VA_SLAVE_ID_STRING;
const std::string SignalSourceID = "audioinput" + std::to_string(Channel);
//TODO: Check if signal source really exist, otherwise return invalid ID
//PSC: I was trying to use CVASignalSourceInfo for that. But when the local variable is destroyed, the program crashes.
//std::vector<CVASignalSourceInfo> Infos;
//VAServer->GetSignalSourceInfos(Infos);
//CVASignalSourceInfo Info = VAServer->GetSignalSourceInfo(SignalSourceID);
//if (false)
// return VA_INVALID_ID_STRING;
return SignalSourceID;
}
std::string FVAPlugin::CreateSignalSourcePrototype(UVAAbstractSignalSource* SignalSource)
{
if (!ShouldInteractWithServer())
......@@ -1237,8 +1259,8 @@ void FVAPlugin::SetScale(const float ScaleN)
void FVAPlugin::SetUseVA(const bool bUseVAN)
{
// VA cannot be activated once it was deactivated
if(bUseVA == false || bUseVA == bUseVAN)
// VA cannot be activated once it was deactivated (we need to check bPluginInitialized because we can reset it on beginning a new session)
if((bUseVA == false && bPluginInitialized) || bUseVA == bUseVAN)
{
return;
}
......
......@@ -76,6 +76,9 @@ public:
static bool SetSignalSourceBufferPlaybackPosition(const std::string& SignalSourceID, float Time);
static bool SetSignalSourceBufferLooping(const std::string& SignalSourceID, bool bLoop);
// Returns the ID of the signal source referring the given audio input channel. Invalid ID, if channel does not exist
static std::string GetAudioInputSignalSourceID(const int Channel);
static std::string CreateSignalSourcePrototype(UVAAbstractSignalSource* SignalSource);
// Deletes a signal source with given ID. Use with great care!
static bool DeleteSignalSource(const std::string& SignalSourceID);
......
......@@ -57,7 +57,7 @@ void AVAReceiverActor::BeginPlay()
if (bAutomaticRemoteVAStart)
{
FVAPlugin::VAServerLauncher.StartVAServerLauncher(); //if possible
bStartedVAServer = FVAPlugin::VAServerLauncher.RemoteStartVAServer(GetIPAddress(), RemoteVAStarterPort, WhichVAServerVersionToStart);
bStartedVAServer = FVAPlugin::VAServerLauncher.RemoteStartVAServer(GetIPAddress(), RemoteVAStarterPort, WhichVAServerVersionToStart, VARendererIniFile, ReproductionInputType);
if(bStartedVAServer){
FVAPlugin::SetUseVA(true);
}
......@@ -257,13 +257,28 @@ bool AVAReceiverActor::UpdateVirtualWorldPose()
if(TrackingSource == ETrackingSource::VirtualRealityPawn)
{
// Auralization Pose is coppled to the Virtual Reality Pawn, e.g, HMD, CAVE
FVector ViewPos;
FRotator ViewRot;
GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(ViewPos, ViewRot);
// Auralization Pose is coupled to the Virtual Reality Pawn, e.g, HMD, CAVE
AVirtualRealityPawn* VirtualRealityPawn = Cast<AVirtualRealityPawn>(GetWorld()->GetFirstPlayerController()->AcknowledgedPawn);
if (VirtualRealityPawn == nullptr)
{
return false;
}
USceneComponent* Head = VirtualRealityPawn->Head;
if (!Head)
{
return false;
}
FVector ViewPos = Head->GetComponentLocation();
FRotator ViewRot = Head->GetComponentRotation();
//Offset from ViewPoint (between eyes) to head center, rotate according to view rotation
ViewPos += ViewRot.RotateVector(ViewpointToHeadcenterOffset);
bool bResult = true;
bResult &= FVAPlugin::SetSoundReceiverPosition(ReceiverID, ViewPos + ViewRot.RotateVector(ViewpointToHeadcenterOffset)); //Offset from ViewPoint (between eyes) to head center, rotate according to view rotation
bResult &= FVAPlugin::SetSoundReceiverPosition(ReceiverID, ViewPos);
bResult &= FVAPlugin::SetSoundReceiverRotation(ReceiverID, ViewRot);
return bResult;
}
......@@ -518,6 +533,14 @@ bool AVAReceiverActor::CanEditChange(const FProperty* InProperty) const
return AddressSetting == EConnectionSetting::Manual;
}
if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(AVAReceiverActor, RemoteVAStarterPort) ||
InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(AVAReceiverActor, WhichVAServerVersionToStart) ||
InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(AVAReceiverActor, VARendererIniFile) ||
InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(AVAReceiverActor, ReproductionInputType))
{
return bAutomaticRemoteVAStart;
}
if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(AVAReceiverActor, DirMappingFileName))
{
return bReadInitialMappingFile;
......
......@@ -11,9 +11,10 @@
#include "VAUtils.h"
#include "VASettings.h"
#include "VAPlugin.h"
#include "HAL/FileManagerGeneric.h"
bool FVAServerLauncher::RemoteStartVAServer(const FString& Host, const int Port, const FString& VersionName)
bool FVAServerLauncher::RemoteStartVAServer(const FString& Host, const int Port, const FString& VersionName, const FString& VARendererIniFile, const EReproductionInput ReproductionInputType)
{
if (!UVirtualRealityUtilities::IsMaster())
{
......@@ -53,6 +54,24 @@ bool FVAServerLauncher::RemoteStartVAServer(const FString& Host, const int Port,
}
FVAUtils::LogStuff("[FVAServerLauncher::RemoteStartVAServer()]: Successfully connected to Launcher", false);
const bool bSendVARendererIni = !VARendererIniFile.IsEmpty();
if (bSendVARendererIni)
{
if (!SendFileToVAServer(VARendererIniFile))
{
FVAUtils::OpenMessageBox("[FVAServerLauncher::RemoteStartVAServer()]: VARenderer.ini file '" + VARendererIniFile +
"' could not be copied to VAServerLauncher. Does the file exist? See error log for additional info. VAServerLauncher will run with default settings.", true);
}
}
if (!SendReproductionInputSignalType(ReproductionInputType))
{
FVAUtils::OpenMessageBox("[FVAServerLauncher::RemoteStartVAServer()]: ReproductionInputType '" + EnumToString(ReproductionInputType) +
"' could not be sent to VAServerLauncher. See error log for additional info. VAServerLauncher will run with default settings.", true);
}
//Send requested version
TArray<uint8> RequestData = ConvertString(VersionName);
int BytesSend = 0;
......@@ -102,6 +121,7 @@ bool FVAServerLauncher::RemoteStartVAServer(const FString& Host, const int Port,
VAServerLauncherSocket = nullptr;
return false;
}
return true;
}
......@@ -128,11 +148,38 @@ bool FVAServerLauncher::StartVAServerLauncher()
FString LauncherScript = TEXT("VirtualAcousticsStarterServer.py");
if (FPaths::FileExists(FPaths::Combine(LauncherScriptDir, LauncherScript)))
{
FString command = "cd/d "+ LauncherScriptDir+" & start python " + LauncherScript;
system(TCHAR_TO_ANSI(*command));
FString CMDCommand = "cd/d " + LauncherScriptDir + " & ";
//check whether py or python exist
auto DoesCommandExist = [&](FString Command)
{
//this checks whether a given command returns a result
FString TmpCmdResultFile = "tmpPyVersion.txt";
TmpCmdResultFile = FPaths::Combine(LauncherScriptDir, TmpCmdResultFile);
Command = Command + " >> \"" + TmpCmdResultFile + "\"";
system(TCHAR_TO_ANSI(*Command));
FString Result;
FFileHelper::LoadFileToString(Result, *TmpCmdResultFile);
IFileManager::Get().Delete(*TmpCmdResultFile);
return !Result.IsEmpty();
};
bool bPyExists = DoesCommandExist("py --version");
bool bPythonExists = DoesCommandExist("python --version");
if(bPythonExists || bPyExists)
{
FString Command = CMDCommand + "start " + (bPythonExists?"python":"py") + " " + LauncherScript;
system(TCHAR_TO_ANSI(*Command));
return true;
}
else
{
FVAUtils::OpenMessageBox("VA Launcher cannot be started since neither \"py\" nor \"python\" can be found. If it is installed add it to PATH (and restart Visual Studio)", true);
return false;
}
}
else
{
FVAUtils::LogStuff("[FVAServerLauncher::StartVAServerLauncher] Unable to automatically start the launcher script, looked for "+LauncherScript+" at "+LauncherScriptDir+". If you want to use this convenience function change the VALauncher Path in the Engine/Virtual Acoustics(VA) section of the project settings. However, nothing bad will happen without.");
}
......@@ -167,15 +214,23 @@ bool FVAServerLauncher::SendFileToVAServer(const FString& RelativeFilename)
TArray<uint8> FileBinaryArray;
FFileHelper::LoadFileToArray(FileBinaryArray, *FPaths::Combine(FPaths::ProjectContentDir(),RelativeFilename));
FString FullPath = FPaths::Combine(FPaths::ProjectContentDir(), RelativeFilename);
const TCHAR* charFilePath = *FullPath;
FFileManagerGeneric fm;
FDateTime LastModification = fm.GetTimeStamp(charFilePath);
const FString ProjectName = GetDefault<UGeneralProjectSettings>()->ProjectName;
FString MetaInfo = "file:"+RelativeFilename+":"+FString::FromInt(FileBinaryArray.Num())+":"+ProjectName;
FString MetaInfo = "file:"
+RelativeFilename+":"
+FString::FromInt(FileBinaryArray.Num())+":"
+ProjectName+":"
+FString::FromInt(LastModification.ToUnixTimestamp());
TArray<uint8> MetaInfoBinary = ConvertString(MetaInfo);
int32 BytesSend;
VAServerLauncherSocket->Send(MetaInfoBinary.GetData(), MetaInfoBinary.Num(), BytesSend);
//Receive response
const int32 BufferSize = 16;
const int32 BufferSize = 128;
int32 BytesRead = 0;
uint8 Response[16];
if (VAServerLauncherSocket->Recv(Response, BufferSize, BytesRead))
......@@ -193,7 +248,8 @@ bool FVAServerLauncher::SendFileToVAServer(const FString& RelativeFilename)
}
FVAUtils::LogStuff("[FVAServerLauncher::SendFileToVAServer()]: Entire file ("+RelativeFilename+") send!", false);
VAServerLauncherSocket->Recv(Response, BufferSize, BytesRead);
if(BytesRead==3 && Response[0]=='a' && Response[1]=='c' && Response[2]=='k')
ResponseString = ByteArrayToString(Response, BytesRead);
if(ResponseString == "ack")
{
FVAUtils::LogStuff("[FVAServerLauncher::SendFileToVAServer()]: File was received by VAServerLauncher successfully!", false);
//the search path is added potenitally multiple times, but can only be added once the folder is created (which the above guarantees)
......@@ -244,6 +300,33 @@ bool FVAServerLauncher::IsVAServerLauncherConnected()
return VAServerLauncherSocket!=nullptr;
}
bool FVAServerLauncher::SendReproductionInputSignalType(const EReproductionInput ReproductionInputType)
{
const FString ProjectName = GetDefault<UGeneralProjectSettings>()->ProjectName;
FString CommandMsg = "reproduction_input_type:" + EnumToString(ReproductionInputType);
TArray<uint8> CommandMsgBinary = ConvertString(CommandMsg);
int32 BytesSend;
VAServerLauncherSocket->Send(CommandMsgBinary.GetData(), CommandMsgBinary.Num(), BytesSend);
const int32 BufferSize = 16;
int32 BytesRead = 0;
uint8 Response[16];
if (VAServerLauncherSocket->Recv(Response, BufferSize, BytesRead))
{
FString ResponseString = ByteArrayToString(Response, BytesRead);
if (ResponseString == "ack")
{
FVAUtils::LogStuff("[FVAServerLauncher::SendReproductionInputSignalType()]: ReproductionInputType was successfully received by VAServerLauncher!", false);
return true;
}
FVAUtils::LogStuff("[FVAServerLauncher::SendReproductionInputSignalType()]: ReproductionInputType not accepted by VAServerLauncher! Answer: " + ResponseString, false);
return false;
}
FVAUtils::LogStuff("[FVAServerLauncher::SendReproductionInputSignalType()]: VAServerLauncher did not answer!", true);
return false;
}
TArray<uint8> FVAServerLauncher::ConvertString(const FString& String)
{
TArray<uint8> RequestData;
......@@ -275,3 +358,19 @@ FString FVAServerLauncher::ByteArrayToString(const uint8* In, int32 Count)
}
return Result;
}
FString FVAServerLauncher::EnumToString(EReproductionInput Enum)
{
switch (Enum)
{
case EReproductionInput::Binaural:
return TEXT("binaural");
case EReproductionInput::Ambisonics:
return TEXT("ambisonics");
case EReproductionInput::Custom:
return TEXT("custom");
default:
return TEXT("invalid");
}
}
......@@ -2,12 +2,14 @@
#include "Sockets.h"
#include "VAEnums.h"
class FVAServerLauncher
{
public:
// Remote Start VAServer
bool RemoteStartVAServer(const FString& Host, int Port,
const FString& VersionName);
const FString& VersionName, const FString& VARendererIni, const EReproductionInput ReproductionInputType);
bool StartVAServerLauncher();
......@@ -22,9 +24,13 @@ public:
private:
bool SendReproductionInputSignalType(const EReproductionInput ReproductionInputType);
TArray<uint8> ConvertString(const FString& String);
FString ByteArrayToString(const uint8* In, int32 Count);
static FString EnumToString(EReproductionInput Enum);
//Socket connection to the VAServerLauncher, has to be held open until the program ends
FSocket* VAServerLauncherSocket=nullptr;
......
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "SignalSources/VAAbstractSignalSource.h"
#include "VAAudioInputSignalSource.generated.h"
/**
*
*/
UCLASS(ClassGroup = (VA))
class VAPLUGIN_API UVAAudioInputSignalSource : public UVAAbstractSignalSource
{
GENERATED_BODY()
protected:
// Input channel used to stream into signal source
UPROPERTY(EditAnywhere, meta = (DisplayName = "Input Channel ID", Category = "Audio Input", ClampMin = "1"))
int Channel = 1;
public:
UVAAudioInputSignalSource() = default;
// Creates the signal source in VA and sets the ID accordingly
void Initialize() override;
};
......@@ -5,10 +5,13 @@
#include "VAEnums.h"
#include "../../Private/SignalSources/VAAudiofileManager.h"
#include "Events/DisplayClusterEventWrapper.h"
#include <string>
#include "CoreMinimal.h"
#include "SignalSources/VAAbstractSignalSource.h"
#include "VAAudiofileSignalSource.generated.h"
......@@ -22,6 +25,7 @@ class VAPLUGIN_API UVAAudiofileSignalSource : public UVAAbstractSignalSource
public:
UVAAudiofileSignalSource() = default;
~UVAAudiofileSignalSource();
// Creates the signal source in VA and sets the ID accordingly
void Initialize() override;
......@@ -58,8 +62,6 @@ public:
bool SetLoop(bool bLoopN);
UFUNCTION(BlueprintCallable)
bool SetPlayBackPosition(float Time);
UFUNCTION(BlueprintCallable)
bool SetPlayAction(int Action);
// *** Getter *** //
......@@ -68,10 +70,16 @@ public:
FString GetFilename() const;
UFUNCTION(BlueprintCallable)
bool GetLoop() const;
//CAUTION when used in cluster mode (e.g., CAVE):
// GetPlayActionEnum() might be one tick behind in cluster mode, since the current play action is only synced between master and slaves after this is called
// so make sure to, e.g., call this every frame or at least multiple times (if you, e.g., want to observe a change from play to pause)
// Alternatively, you can also set the bDirectOnMaster flag to get the status directly from the master, in that case you should make sure
// that everything is synced to the slaves, e.g., using sync components, as data will be delayed on the slaves!!!
UFUNCTION(BlueprintCallable)
EPlayAction::Type GetPlayActionEnum() const;
EPlayAction::Type GetPlayActionEnum(bool bDirectOnMaster = false);
int GetPlayAction() const;
// *** Events/Delegates *** //
......@@ -84,11 +92,6 @@ protected:
// @return True on success
bool CopySignalSourceSettings(const std::string& ID);
private:
FChangedAudiofileEvent AudiofileChangedEvent;
protected:
// Action of the sound source at the first tick
UPROPERTY(EditAnywhere, meta = (DisplayName = "Starting State", Category = "Audio file"))
TEnumAsByte<EPlayAction::Type> StartingPlayAction = EPlayAction::Type::Stop;
......@@ -108,5 +111,16 @@ protected:
bool bLoop = false;
private:
bool SetPlayAction(int Action);
int GetPlayAction(bool bDirectOnMaster=false);
void StorePlayStateInternally(int PlayAction);
DECLARE_DISPLAY_CLUSTER_EVENT(UVAAudiofileSignalSource, StorePlayStateInternally);
int InterallyStoredPlayAction = -1;
FChangedAudiofileEvent AudiofileChangedEvent;
FVAAudiofileManager AudiofileManager;
};
......@@ -157,6 +157,12 @@ public:
UFUNCTION(BlueprintCallable)
bool SetDirectivityByFileName(FString FileName);
UFUNCTION(BlueprintCallable)
bool SetDefaultDirectivity();
UFUNCTION(BlueprintCallable)
bool SetNoDirectivity();
// Gets the File Name of the Directivity
UFUNCTION(BlueprintCallable)
FString GetDirectivityFileName() const;
......@@ -185,6 +191,7 @@ protected:
// initialize Sound Source with the settings set //
virtual void Initialize();
void UpdateSkeletalMeshIfAttachToBone();
void UpdatePose();
bool SetSignalSourceID(const std::string& ID);
......@@ -198,6 +205,7 @@ protected:
// Class data
bool bFirstTick = true;
bool bInitialized = false;
bool bInformedNotInitialized = false;
float TimeSinceUpdate;
bool ShouldSendCommand() const;
float Timer;
......
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "SoundSource/VAAbstractSourceComponent.h"
#include "VAAudioInputSourceComponent.generated.h"
/**
*
*/
UCLASS(ClassGroup = (VA), meta = (BlueprintSpawnableComponent))
class VAPLUGIN_API UVAAudioInputSourceComponent : public UVAAbstractSourceComponent
{
GENERATED_BODY()
public:
UVAAudioInputSourceComponent();
};
......@@ -34,6 +34,17 @@ namespace EConnectionSetting
};
}
UENUM()
enum class EReproductionInput : uint8
{
// Use reproduction modules for binaural signals
Binaural,
// Use reproduction modules for ambisonics signals
Ambisonics,
// Use reproduction modules for custom purposes (e.g. mixed signal types)
Custom
};
UENUM(BlueprintType)
namespace EDirectivitySetting
......
......@@ -46,28 +46,55 @@ protected:
UPROPERTY(EditAnywhere, meta = (DisplayName = "Ask for Debug mode?", Category = "General Settings"))
bool bAskForDebugMode = false;
// Check if should try to use Python Automatic Remote star
UPROPERTY(EditAnywhere, meta = (DisplayName = "Use Automatic Remote VA Start via Python?", Category = "General Settings"))
bool bAutomaticRemoteVAStart = true;
// View point to head center offset (x forward-dir, y right-dir, z up-dir) in cm. Note that x and z most probably need to be negative!
UPROPERTY(EditAnywhere, meta = (Category = "General Settings"))
FVector ViewpointToHeadcenterOffset = FVector(0, 0, 0);
// Source for tracking data, e.g., VirtualRealityPawn, ManualData (e.g., manually passed on from Optitrack)
UPROPERTY(EditAnywhere, meta = (Category = "General Settings"))
ETrackingSource TrackingSource = ETrackingSource::VirtualRealityPawn;
// Choose how to connect to the Server (automatic: build with windows connect with 127.0.0.1:12340, build with linux connect to cave)
UPROPERTY(EditAnywhere, meta = (DisplayName = "Usecase", Category = "Connection"))
TEnumAsByte<EConnectionSetting::Type> AddressSetting = EConnectionSetting::Type::Automatic;
// IP Address for manual input
UPROPERTY(EditAnywhere, meta = (DisplayName = "IP Adress", Category = "Connection")) // CanEditChange used
UPROPERTY(EditAnywhere, meta = (DisplayName = "IP Adress", Category = "Connection"))
FString ServerIPAddress = "10.0.1.240";
// Port for manual input
UPROPERTY(EditAnywhere, meta = (DisplayName = "Port [0, 65535]", Category = "Connection", // CanEditChange used
UPROPERTY(EditAnywhere, meta = (DisplayName = "Port [0, 65535]", Category = "Connection",
ClampMin = "0", ClampMax = "65535", UIMin = "0", UIMax = "65535"))
uint16 ServerPort = 12340;
// Port for manual input
UPROPERTY(EditAnywhere, meta = (Category = "Connection", DisplayName = "Always reconnect to VAServer when this level is started?"))
// Always reconnect to VAServer when this level is started?
UPROPERTY(EditAnywhere, meta = (Category = "Connection", DisplayName = "Automatic Reconnect"))
bool bReconnecToVAServer = true;
// Read in File?
// Activate automatic remote VAServer start via Python (VAServerLauncher)
UPROPERTY(EditAnywhere, meta = (DisplayName = "Use VAServerLauncher", Category = "VAServerLauncher"))
bool bAutomaticRemoteVAStart = true;
// Port for remote VAServerLauncher [0, 65535]
UPROPERTY(EditAnywhere, meta = (DisplayName = "VAServerLauncher Port", Category = "VAServerLauncher",
ClampMin = "0", ClampMax = "65535", UIMin = "0", UIMax = "65535"))
uint16 RemoteVAStarterPort = 41578;
// ID for VAServer version being started automatically, configurable in the Config of the VAServerLauncher
UPROPERTY(EditAnywhere, meta = (DisplayName = "VAServer Version ID", Category = "VAServerLauncher"))
FString WhichVAServerVersionToStart = TEXT("2022.a");
// Ini file with VA renderer settings. File will be sent to VAServer launcher on startup if filename is not empty.
UPROPERTY(EditAnywhere, meta = (DisplayName = "VARenderer.ini file", Category = "VAServerLauncher"))
FString VARendererIniFile = TEXT("");
//Used to select the group of reproduction modules specified in VAServerLauncher config.
UPROPERTY(EditAnywhere, meta = (DisplayName = "Reproduction input signal type", Category = "VAServerLauncher"))
EReproductionInput ReproductionInputType = EReproductionInput::Binaural;
// Read an initial mapping file for directivities?
UPROPERTY(EditAnywhere, meta = (DisplayName = "Read an initial mapping file?", Category = "Directivity Manager"))
bool bReadInitialMappingFile = false;
......@@ -76,31 +103,10 @@ protected:
UPROPERTY(EditAnywhere, meta = (DisplayName = "Name of ini file for directivities", Category = "Directivity Manager"))
FString DirMappingFileName = "";
// Port for remote VAServer starting
UPROPERTY(EditAnywhere, meta = (DisplayName = "Remote VAServer Start Port [0, 65535]", Category = "Connection",
// CanEditChange used
ClampMin = "0", ClampMax = "65535", UIMin = "0", UIMax = "65535"))
uint16 RemoteVAStarterPort = 41578;
// Which version should be started automatically
UPROPERTY(EditAnywhere, meta = (DisplayName =
"Which VAServer version should be started, configurable in the Config of the VAServer Launcher", Category =
"General Settings"))
FString WhichVAServerVersionToStart = TEXT("2020.a");
// Controller for global auraliztion modes
UPROPERTY(EditAnywhere, Instanced, NoClear, meta = (DisplayName = "Auraliztion Mode Controller", Category = "Auralization Modes"))
UVAAuralizationModeController* AuralizationModeController;
// View point to head center offset (x forward-dir, y right-dir, z up-dir) in cm. Note that x and z most probably need to be negative!
UPROPERTY(EditAnywhere, meta = (Category = "General Settings"))
FVector ViewpointToHeadcenterOffset = FVector(0, 0, 0);
// Source for tracking data, e.g., VirtualRealityPawn, ManualData (e.g., manually passed on from Optitrack)
UPROPERTY(EditAnywhere, meta = (Category = "General Settings"))
ETrackingSource TrackingSource = ETrackingSource::VirtualRealityPawn;
public:
// Sets default values for this actor's properties
......
......@@ -11,7 +11,7 @@ class UVASettings : public UDeveloperSettings
public:
UPROPERTY(EditAnywhere, config, Category = "General", meta = (DisplayName = "Relative Path of this project to VAServer"))
FString VALauncherPath = "../VAServer";
FString VALauncherPath = "../VAServerLauncher";
UPROPERTY(EditAnywhere, config, Category = "General", meta = (DisplayName = "Copy audio files over network to VAServer"))
bool VALauncherCopyFiles = true;
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment