diff --git a/OSC/Source/OSC/Private/Common/OscDataElemStruct.h b/OSC/Source/OSC/Private/Common/OscDataElemStruct.h index a4065e8..fe66b6f 100644 --- a/OSC/Source/OSC/Private/Common/OscDataElemStruct.h +++ b/OSC/Source/OSC/Private/Common/OscDataElemStruct.h @@ -8,24 +8,20 @@ struct FOscDataElemStruct { GENERATED_USTRUCT_BODY() - UPROPERTY() - int32 Type; - - UPROPERTY() - int64 Data; - - FOscDataElemStruct() : Type(0), Data(0) - { } - enum { - FLOAT = 0, - INT = 1, - BOOL = 2, + FLOAT = 0, + INT = 1, + BOOL = 2, STRING = 3, + BLOB = 4, }; - - //------------------------------------------------------------------------- + +public: + + FOscDataElemStruct() : Type(0), Data(0) + { + } void SetFloat(double value) { @@ -112,6 +108,27 @@ struct FOscDataElemStruct //------------------------------------------------------------------------- + void SetBlob(TArray value) + { + Data = 0; + Type = BLOB; + Blob = MakeShared>(std::move(value)); + } + + const TArray & AsBlobValue() const + { + check(IsBlob()); + static const TArray empty; + return Blob.IsValid() ? *Blob : empty; + } + + bool IsBlob() const + { + return Type == BLOB; + } + + //------------------------------------------------------------------------- + /// Cast the value to T. template T GetValue() const @@ -150,6 +167,14 @@ struct FOscDataElemStruct return Cast::eval(value); } } + else if(IsBlob()) + { + const bool isBlob = std::is_same>::value; + if(isBlob) + { + return Cast::eval(AsBlobValue()); + } + } return T(); } @@ -163,6 +188,13 @@ private: return T(); } }; + +private: + int32 Type; + + int64 Data; + + TSharedPtr> Blob; }; diff --git a/OSC/Source/OSC/Private/Common/OscFunctionLibrary.cpp b/OSC/Source/OSC/Private/Common/OscFunctionLibrary.cpp index 2778e48..9ad5416 100644 --- a/OSC/Source/OSC/Private/Common/OscFunctionLibrary.cpp +++ b/OSC/Source/OSC/Private/Common/OscFunctionLibrary.cpp @@ -44,6 +44,11 @@ void UOscFunctionLibrary::PopString(const TArray & input, TA PopValueImpl(input, output, Value); } +void UOscFunctionLibrary::PopBlob(const TArray & input, TArray & output, TArray & Value) +{ + PopValueImpl(input, output, Value); +} + void UOscFunctionLibrary::PushBool(const TArray & input, bool Value, TArray & output) { @@ -84,6 +89,14 @@ void UOscFunctionLibrary::PushString(const TArray & input, F output.Add(elem); } +void UOscFunctionLibrary::PushBlob(const TArray & input, const TArray & Value, TArray & output) +{ + output = input; + FOscDataElemStruct elem; + elem.SetBlob(Value); + output.Add(elem); +} + bool UOscFunctionLibrary::AsBool(const FOscDataElemStruct & input) { @@ -105,6 +118,11 @@ FName UOscFunctionLibrary::AsString(const FOscDataElemStruct & input) return input.GetValue(); } +TArray UOscFunctionLibrary::AsBlob(const FOscDataElemStruct & input) +{ + return input.GetValue>(); +} + FOscDataElemStruct UOscFunctionLibrary::FromBool(bool input) { @@ -134,6 +152,13 @@ FOscDataElemStruct UOscFunctionLibrary::FromString(FName input) return result; } +FOscDataElemStruct UOscFunctionLibrary::FromBlob(const TArray & input) +{ + FOscDataElemStruct result; + result.SetBlob(input); + return result; +} + namespace { @@ -188,6 +213,11 @@ namespace { output << elem.AsStringValue().GetPlainANSIString(); } + else if(elem.IsBlob()) + { + const TArray & value = elem.AsBlobValue(); + output << osc::Blob(value.GetData(), value.Num()); + } if(output.State() != osc::SUCCESS) { diff --git a/OSC/Source/OSC/Private/Common/OscFunctionLibrary.h b/OSC/Source/OSC/Private/Common/OscFunctionLibrary.h index d2b1c59..8fbe6da 100644 --- a/OSC/Source/OSC/Private/Common/OscFunctionLibrary.h +++ b/OSC/Source/OSC/Private/Common/OscFunctionLibrary.h @@ -28,6 +28,10 @@ public: UFUNCTION(BlueprintPure, Category=OSC) static void PopString(const TArray & input, TArray & output, FName & Value); + /// Get the next value from an OSC message as a blob. + UFUNCTION(BlueprintPure, Category=OSC) + static void PopBlob(const TArray & input, TArray & output, TArray & Value); + /// Add a boolean value to an OSC message. UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input")) @@ -45,6 +49,10 @@ public: UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input")) static void PushString(const TArray & input, FName Value, TArray & output); + /// Add a blob to an OSC message. + UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input")) + static void PushBlob(const TArray & input, const TArray & Value, TArray & output); + /// Interpret an OSC argument as a boolean UFUNCTION(BlueprintPure, Category=OSC) @@ -62,6 +70,10 @@ public: UFUNCTION(BlueprintPure, Category=OSC) static FName AsString(const FOscDataElemStruct & input); + /// Interpret an OSC argument as a blob. + UFUNCTION(BlueprintPure, Category=OSC) + static TArray AsBlob(const FOscDataElemStruct & input); + /// Create an OSC argument from a boolean UFUNCTION(BlueprintPure, Category=OSC) @@ -79,6 +91,10 @@ public: UFUNCTION(BlueprintPure, Category=OSC) static FOscDataElemStruct FromString(FName input); + /// Create an OSC argument from a blob. + UFUNCTION(BlueprintPure, Category=OSC) + static FOscDataElemStruct FromBlob(const TArray & input); + /** * @brief Send an OSC message. diff --git a/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp b/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp index 4988f05..7c40908 100644 --- a/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp +++ b/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp @@ -131,6 +131,25 @@ static void SendMessage(TCircularQueueAsStringUnchecked())); } + else if(it->IsBlob()) + { + const void* buffer; + osc::osc_bundle_element_size_t size; + osc::Errors error = osc::SUCCESS; + it->AsBlobUnchecked(buffer, size, error); + + TArray blob; + if(size && !error) + { + blob.SetNumUninitialized(size); + FMemory::Memcpy(blob.GetData(), buffer, size); + } + else if(error) + { + UE_LOG(LogOSC, Warning, TEXT("OSC Received Message Error: %s"), osc::errorString(error)); + } + elem.SetBlob(std::move(blob)); + } data.Add(elem); } diff --git a/README.md b/README.md index 59421b6..316eb61 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ UE4-OSC OSC plugin for Unreal Engine 4 to send and receive OSC messages with blueprints! -Tutorial -======== +# Tutorial Dannington made a super tutorial! @@ -14,13 +13,12 @@ Watch it to properly install the plugin and get some brilliant ideas on how to u https://www.youtube.com/watch?v=GGGs-n-CKtY -Also, go to the UE4 forum: +Also, go to the UE4 forum dedicated thread: https://forums.unrealengine.com/showthread.php?49627-Plugin-OSC-for-UE4 -Integration -=========== +# Integration Install Visual Studio 2015 on Windows (Community edition works) or XCode on MaxOSX. @@ -34,41 +32,68 @@ Run the "*.uproject" file: the plugin is compiled automatically. Alternatively, right-clic the "*.uproject" to generate Visual Studio or XCode project files. -Usage -===== +# Usage - - Receiver: - -Set the listening port in the plugin settings. +### Receiver + +Set the listening port as "Receive From" in the plugin settings. (Edit/Project Settings/Plugins/OSC) Add a OscReceiverComponent to your Blueprint class and bind the OnOscReceived custom event, or +subclass the OscReceiverActor and bind the OnOscReceived custom event. -Subclass the OscReceiverActor and bind the OnOscReceived custom event. +The OnOscReceived event gives: the OSC address, the OSC parameters and the IP of the sender. -Use the PopFloat/Int/Bool/String functions, or standard array functions, to retrieve the OSC message parameters. +The OSC parameters is an array of OSC elements. To read the content, either: + - chain PopFloat/Int/Bool/String/Blob functions to get the first value, or + - use the standard GET function and AsFloat/Int/Bool/String/Blob functions to cast the element to its value. This is slightly more efficient. +### Sender - - Sender: - -Set the "send to" addresses in the plugin settings. +Set the "Send Targets" addresses in the plugin settings. (Edit/Project Settings/Plugins/OSC) -Use the PushFloat/Int/Bool/String functions, or standard array functions, to build the OSC message parameters. +You can also add new targets dynamically, using the "Add Send Osc Target" function. +It is especially useful for "ping"-like behavior. -Use the SendOsc function to send the OSC message +Send a single message with "Send Osc" or a bundle of messages with "Send Osc Bundle". -Use the SendOscBundle function to send an OSC bundle (array of messages) +The "Send Osc" function takes: the OSC address, the OSC parameters and the "target index" of the destination. +Build the OSC parameters for a message: + - chain PushFloat/Int/Bool/String/Blob functions, or + - fill an array of "Osc Data Elem" with the standard array functions and the FromFloat/Int/Bool/String/Blob functions. + +The "target index" refers to an index in the "Send Targets" array in the settings, +or to the result of the "Add Send Osc Target" function. + +### Inputs (experimental) - - Inputs: - List the messages that should be UE4 inputs (like a game controller). A new input is created for each message. Bind OSC inputs to UE4 action in the usual "Input" project settings. -Engine version -===== +# General information + +### Troubleshooting + +The plugin uses the standard UE4 logging system. + +Check your logs (Window/Developers Tools/Output Log) before asking for help! + +### String limitation + +Historically, the plugin handles OSC string using "FName". Unfortunately, the length of +a "FName" is limited to 1024 characters. So this plugin does not handle long strings correctly. + +If you need long strings, prefer using blobs. + +### Blob + +The OSC protocol supports buffers as "blob". + +This plugin implements them as "Array". It plays nicely with the "Extended Standard Library" plugin by "Low Entry". + + +# Engine version Get the right branch for your engine version: "master" for the latest version, "before4.8" for 4.4, ..., 4.7, etc. - -Optionally, set the OSC_ENGINE_VERSION value (in Source/OSC/Private/Osc.h) to compile for a specific engine version.