From 1d352e7216b27e1fa402aa2859b3733d8d048f11 Mon Sep 17 00:00:00 2001 From: monsieurgustav Date: Sun, 5 Oct 2014 19:45:30 +0200 Subject: [PATCH] initial commit --- OSC/OSC.uplugin | 25 + OSC/Resources/Icon128.png | Bin 0 -> 16284 bytes OSC/Source/OSC/OSC.Build.cs | 39 + .../OSC/Private/Common/OscDataElemStruct.h | 191 +++++ OSC/Source/OSC/Private/Common/OscDataStruct.h | 31 + .../OSC/Private/Common/OscFunctionLibrary.cpp | 106 +++ .../OSC/Private/Common/OscFunctionLibrary.h | 54 ++ .../Private/Common/oscpack/osc/OscException.h | 62 ++ .../Common/oscpack/osc/OscHostEndianness.h | 127 +++ .../oscpack/osc/OscOutboundPacketStream.cpp | 686 +++++++++++++++ .../oscpack/osc/OscOutboundPacketStream.h | 154 ++++ .../oscpack/osc/OscReceivedElements.cpp | 799 ++++++++++++++++++ .../Common/oscpack/osc/OscReceivedElements.h | 548 ++++++++++++ .../Private/Common/oscpack/osc/OscTypes.cpp | 55 ++ .../OSC/Private/Common/oscpack/osc/OscTypes.h | 240 ++++++ OSC/Source/OSC/Private/Osc.h | 11 + OSC/Source/OSC/Private/OscModule.cpp | 77 ++ OSC/Source/OSC/Private/OscPrivatePCH.h | 20 + OSC/Source/OSC/Private/OscSettings.cpp | 58 ++ OSC/Source/OSC/Private/OscSettings.h | 30 + .../OSC/Private/Receive/OscDispatcher.cpp | 181 ++++ .../OSC/Private/Receive/OscDispatcher.h | 48 ++ .../Private/Receive/OscReceiverComponent.cpp | 32 + .../Private/Receive/OscReceiverComponent.h | 38 + .../Private/Receive/OscReceiverInterface.cpp | 18 + .../Private/Receive/OscReceiverInterface.h | 19 + README.md | 22 + 27 files changed, 3671 insertions(+) create mode 100644 OSC/OSC.uplugin create mode 100644 OSC/Resources/Icon128.png create mode 100644 OSC/Source/OSC/OSC.Build.cs create mode 100644 OSC/Source/OSC/Private/Common/OscDataElemStruct.h create mode 100644 OSC/Source/OSC/Private/Common/OscDataStruct.h create mode 100644 OSC/Source/OSC/Private/Common/OscFunctionLibrary.cpp create mode 100644 OSC/Source/OSC/Private/Common/OscFunctionLibrary.h create mode 100644 OSC/Source/OSC/Private/Common/oscpack/osc/OscException.h create mode 100644 OSC/Source/OSC/Private/Common/oscpack/osc/OscHostEndianness.h create mode 100644 OSC/Source/OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.cpp create mode 100644 OSC/Source/OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.h create mode 100644 OSC/Source/OSC/Private/Common/oscpack/osc/OscReceivedElements.cpp create mode 100644 OSC/Source/OSC/Private/Common/oscpack/osc/OscReceivedElements.h create mode 100644 OSC/Source/OSC/Private/Common/oscpack/osc/OscTypes.cpp create mode 100644 OSC/Source/OSC/Private/Common/oscpack/osc/OscTypes.h create mode 100644 OSC/Source/OSC/Private/Osc.h create mode 100644 OSC/Source/OSC/Private/OscModule.cpp create mode 100644 OSC/Source/OSC/Private/OscPrivatePCH.h create mode 100644 OSC/Source/OSC/Private/OscSettings.cpp create mode 100644 OSC/Source/OSC/Private/OscSettings.h create mode 100644 OSC/Source/OSC/Private/Receive/OscDispatcher.cpp create mode 100644 OSC/Source/OSC/Private/Receive/OscDispatcher.h create mode 100644 OSC/Source/OSC/Private/Receive/OscReceiverComponent.cpp create mode 100644 OSC/Source/OSC/Private/Receive/OscReceiverComponent.h create mode 100644 OSC/Source/OSC/Private/Receive/OscReceiverInterface.cpp create mode 100644 OSC/Source/OSC/Private/Receive/OscReceiverInterface.h diff --git a/OSC/OSC.uplugin b/OSC/OSC.uplugin new file mode 100644 index 0000000..7efc579 --- /dev/null +++ b/OSC/OSC.uplugin @@ -0,0 +1,25 @@ +{ + "FileVersion" : 3, + + "FriendlyName" : "OSC", + "Version" : 1, + "VersionName" : "1.0", + "CreatedBy" : "Guillaume Buisson", + "CreatedByURL" : "http://guillaume.io", + "EngineVersion" : "4.4.0", + "Description" : "", + "Category" : "OSC", + "EnabledByDefault" : false, + + "Modules" : + [ + { + "Name" : "OSC", + "Type" : "Runtime", + "LoadingPhase" : "PreDefault", + "BlacklistPlatforms" : [ "HTML5" ] + } + ], + + "CanContainContent" : false +} \ No newline at end of file diff --git a/OSC/Resources/Icon128.png b/OSC/Resources/Icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..12c0099d40a3564709b3606514503db65c2a43b6 GIT binary patch literal 16284 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 +#include "OscDataElemStruct.generated.h" + + +USTRUCT() +struct FOscDataElemStruct +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY() + int32 Type; + + UPROPERTY() + int64 Data; + + FOscDataElemStruct() : Type(0), Data(0) + { } + + enum + { + FLOAT = 0, + INT = 1, + BOOL = 2, + STRING = 3, + }; + + //------------------------------------------------------------------------- + + void SetFloat(double value) + { + static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the float value."); + Type = FLOAT; + Data = *reinterpret_cast(&value); + assert(value == AsFloatValue()); + } + + double AsFloatValue() const + { + return *reinterpret_cast(&Data); + } + + bool IsFloat() const + { + return Type == FLOAT; + } + + //------------------------------------------------------------------------- + + void SetInt(int64 value) + { + static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the integer value."); + Type = INT; + Data = value; + assert(value == AsIntValue()); + } + + int64 AsIntValue() const + { + return Data; + } + + bool IsInt() const + { + return Type == INT; + } + + //------------------------------------------------------------------------- + + void SetBool(bool value) + { + static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the bool value."); + Type = BOOL; + Data = value; + assert(value == AsBoolValue()); + } + + bool AsBoolValue() const + { + return Data != 0; + } + + bool IsBool() const + { + return Type == BOOL; + } + + //------------------------------------------------------------------------- + + void SetString(FName value) + { + static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the string value."); + Type = STRING; + Data = *reinterpret_cast(&value); + assert(value == AsStringValue()); + } + + FName AsStringValue() const + { + return *reinterpret_cast(&Data); + } + + bool IsString() const + { + return Type == STRING; + } + + //------------------------------------------------------------------------- + + template + T GetValue() const + { + static const bool isNumber = (std::is_integral::value || std::is_floating_point::value); + if(IsFloat()) + { + if(isNumber) + { + const auto value = AsFloatValue(); + return Cast::eval(value); + } + } + else if(IsInt()) + { + if(isNumber) + { + const auto value = AsIntValue(); + return Cast::eval(value); + } + } + else if(IsBool()) + { + if(isNumber) + { + const auto value = AsIntValue(); + return Cast::eval(value); + } + } + else if(IsString()) + { + const bool isString = std::is_same::value; + if(isString) + { + const auto value = AsStringValue(); + return Cast::eval(value); + } + } + return T(); + } + +private: + template + struct Cast + { + template + static T eval(const U &) + { + return T(); + } + }; +}; + + +template +struct FOscDataElemStruct::Cast +{ + static T eval(double value) + { + if(std::is_integral::value) + { + value += 0.4999999; + } + return static_cast(value); + } + + template + static T eval(const U & value) + { + return static_cast(value); + } +}; + +template <> +struct FOscDataElemStruct::Cast +{ + template + static bool eval(const U & value) + { + return value != 0; + } +}; diff --git a/OSC/Source/OSC/Private/Common/OscDataStruct.h b/OSC/Source/OSC/Private/Common/OscDataStruct.h new file mode 100644 index 0000000..5ce5ec4 --- /dev/null +++ b/OSC/Source/OSC/Private/Common/OscDataStruct.h @@ -0,0 +1,31 @@ +#pragma once + +#include "OscDataElemStruct.h" +#include "OscDataStruct.generated.h" + + +USTRUCT() +struct FOscDataStruct +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY() + int32 Index; + + UPROPERTY() + TArray Queue; + + FOscDataStruct() : Index(0) + { } + + FOscDataElemStruct Pop() + { + const auto idx = std::max(Index, 0); + if(idx < Queue.Num()) + { + ++Index; + return Queue[idx]; + } + return FOscDataElemStruct(); + } +}; diff --git a/OSC/Source/OSC/Private/Common/OscFunctionLibrary.cpp b/OSC/Source/OSC/Private/Common/OscFunctionLibrary.cpp new file mode 100644 index 0000000..3c26e61 --- /dev/null +++ b/OSC/Source/OSC/Private/Common/OscFunctionLibrary.cpp @@ -0,0 +1,106 @@ +#include "OscPrivatePCH.h" +#include "OscFunctionLibrary.h" +#include "oscpack/osc/OscOutboundPacketStream.h" + + +UOscFunctionLibrary::UOscFunctionLibrary(const class FPostConstructInitializeProperties& PCIP) + : Super(PCIP) +{ +} + +template +static inline +void PopValueImpl(const FOscDataStruct & input, FOscDataStruct & output, T & Value) +{ + output = input; + Value = output.Pop().GetValue(); +} + +void UOscFunctionLibrary::PopBool(const FOscDataStruct & input, FOscDataStruct & output, bool & Value) +{ + PopValueImpl(input, output, Value); +} + +void UOscFunctionLibrary::PopFloat(const FOscDataStruct & input, FOscDataStruct & output, float & Value) +{ + PopValueImpl(input, output, Value); +} + +void UOscFunctionLibrary::PopInt(const FOscDataStruct & input, FOscDataStruct & output, int32 & Value) +{ + PopValueImpl(input, output, Value); +} + +void UOscFunctionLibrary::PopString(const FOscDataStruct & input, FOscDataStruct & output, FName & Value) +{ + PopValueImpl(input, output, Value); +} + + +void UOscFunctionLibrary::PushBool(FOscDataStruct input, bool Value, FOscDataStruct & output) +{ + output = input; + FOscDataElemStruct elem; + elem.SetBool(Value); + output.Queue.Add(elem); +} + +void UOscFunctionLibrary::PushFloat(FOscDataStruct input, float Value, FOscDataStruct & output) +{ + output = input; + FOscDataElemStruct elem; + elem.SetFloat(Value); + output.Queue.Add(elem); +} + +void UOscFunctionLibrary::PushInt(FOscDataStruct input, int32 Value, FOscDataStruct & output) +{ + output = input; + FOscDataElemStruct elem; + elem.SetInt(Value); + output.Queue.Add(elem); +} + +void UOscFunctionLibrary::PushString(FOscDataStruct input, FName Value, FOscDataStruct & output) +{ + output = input; + FOscDataElemStruct elem; + elem.SetString(Value); + output.Queue.Add(elem); +} + +void UOscFunctionLibrary::SendOsc(const FString & Address, const FOscDataStruct & Data, int32 TargetIndex) +{ + static_assert(sizeof(uint8) == sizeof(char), "Cannot cast uint8 to char"); + uint8 buffer[1024]; + + char oscAddress[256]; + strcpy(oscAddress, TCHAR_TO_ANSI(*Address)); + + osc::OutboundPacketStream output((char *)buffer, sizeof(buffer)); + output << osc::BeginMessage(oscAddress); + for(int32 i=Data.Index, n=Data.Queue.Num(); i()->Send(buffer, output.Size(), TargetIndex); +} diff --git a/OSC/Source/OSC/Private/Common/OscFunctionLibrary.h b/OSC/Source/OSC/Private/Common/OscFunctionLibrary.h new file mode 100644 index 0000000..fb192c9 --- /dev/null +++ b/OSC/Source/OSC/Private/Common/OscFunctionLibrary.h @@ -0,0 +1,54 @@ +#pragma once + +#include "OscDataStruct.h" +#include "OscFunctionLibrary.generated.h" + + +UCLASS() +class UOscFunctionLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_UCLASS_BODY() + + /// Get the next value from an OSC message as a boolean. + UFUNCTION(BlueprintPure, Category=OSC) + static void PopBool(const FOscDataStruct & input, FOscDataStruct & output, bool & Value); + + /// Get the next value from an OSC message as a floating point. + UFUNCTION(BlueprintPure, Category=OSC) + static void PopFloat(const FOscDataStruct & input, FOscDataStruct & output, float & Value); + + /// Get the next value from an OSC message as a integer. + UFUNCTION(BlueprintPure, Category=OSC) + static void PopInt(const FOscDataStruct & input, FOscDataStruct & output, int32 & Value); + + /// Get the next value from an OSC message as a string. + UFUNCTION(BlueprintPure, Category=OSC) + static void PopString(const FOscDataStruct & input, FOscDataStruct & output, FName & Value); + + + /// Add a boolean value to an OSC message. + UFUNCTION(BlueprintPure, Category=OSC) + static void PushBool(FOscDataStruct input, bool Value, FOscDataStruct & output); + + /// Add a floating point value to an OSC message. + UFUNCTION(BlueprintPure, Category=OSC) + static void PushFloat(FOscDataStruct input, float Value, FOscDataStruct & output); + + /// Add a integer value to an OSC message. + UFUNCTION(BlueprintPure, Category=OSC) + static void PushInt(FOscDataStruct input, int32 Value, FOscDataStruct & output); + + /// Add a string value to an OSC message. + UFUNCTION(BlueprintPure, Category=OSC) + static void PushString(FOscDataStruct input, FName Value, FOscDataStruct & output); + + + /** + * @brief Send an OSC message. + * @param Address OSC address. + * @param Data result of successive PushFloat/Int/String/etc. + * @param Index index of the destination. (SendTarget list of the plugin settings) + */ + UFUNCTION(BlueprintCallable, Category=OSC) + static void SendOsc(const FString & Address, const FOscDataStruct & Data, int32 TargetIndex); +}; diff --git a/OSC/Source/OSC/Private/Common/oscpack/osc/OscException.h b/OSC/Source/OSC/Private/Common/oscpack/osc/OscException.h new file mode 100644 index 0000000..e9be222 --- /dev/null +++ b/OSC/Source/OSC/Private/Common/oscpack/osc/OscException.h @@ -0,0 +1,62 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ +#ifndef INCLUDED_OSCPACK_OSCEXCEPTION_H +#define INCLUDED_OSCPACK_OSCEXCEPTION_H + +#include + +namespace osc{ + +class Exception : public std::exception { + const char *what_; + +public: + Exception() throw() {} + Exception( const Exception& src ) throw() + : std::exception( src ) + , what_( src.what_ ) {} + Exception( const char *w ) throw() + : what_( w ) {} + Exception& operator=( const Exception& src ) throw() + { what_ = src.what_; return *this; } + virtual ~Exception() throw() {} + virtual const char* what() const throw() { return what_; } +}; + +} // namespace osc + +#endif /* INCLUDED_OSCPACK_OSCEXCEPTION_H */ diff --git a/OSC/Source/OSC/Private/Common/oscpack/osc/OscHostEndianness.h b/OSC/Source/OSC/Private/Common/oscpack/osc/OscHostEndianness.h new file mode 100644 index 0000000..a396c5a --- /dev/null +++ b/OSC/Source/OSC/Private/Common/oscpack/osc/OscHostEndianness.h @@ -0,0 +1,127 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ +#ifndef INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H +#define INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H + +/* + Make sure either OSC_HOST_LITTLE_ENDIAN or OSC_HOST_BIG_ENDIAN is defined + + We try to use preprocessor symbols to deduce the host endianness. + + Alternatively you can define one of the above symbols from the command line. + Usually you do this with the -D flag to the compiler. e.g.: + + $ g++ -DOSC_HOST_LITTLE_ENDIAN ... +*/ + +#if defined(OSC_HOST_LITTLE_ENDIAN) || defined(OSC_HOST_BIG_ENDIAN) + +// endianness defined on the command line. nothing to do here. + +#elif defined(__WIN32__) || defined(WIN32) || defined(WINCE) + +// assume that __WIN32__ is only defined on little endian systems + +#define OSC_HOST_LITTLE_ENDIAN 1 +#undef OSC_HOST_BIG_ENDIAN + +#elif defined(__APPLE__) + +#if defined(__LITTLE_ENDIAN__) + +#define OSC_HOST_LITTLE_ENDIAN 1 +#undef OSC_HOST_BIG_ENDIAN + +#elif defined(__BIG_ENDIAN__) + +#define OSC_HOST_BIG_ENDIAN 1 +#undef OSC_HOST_LITTLE_ENDIAN + +#endif + +#elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) + +// should cover gcc and clang + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + +#define OSC_HOST_LITTLE_ENDIAN 1 +#undef OSC_HOST_BIG_ENDIAN + +#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + +#define OSC_HOST_BIG_ENDIAN 1 +#undef OSC_HOST_LITTLE_ENDIAN + +#endif + +#else + +// gcc defines __LITTLE_ENDIAN__ and __BIG_ENDIAN__ +// for others used here see http://sourceforge.net/p/predef/wiki/Endianness/ +#if (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ + || (defined(__ARMEL__) && !defined(__ARMEB__)) \ + || (defined(__AARCH64EL__) && !defined(__AARCH64EB__)) \ + || (defined(_MIPSEL) && !defined(_MIPSEB)) \ + || (defined(__MIPSEL) && !defined(__MIPSEB)) \ + || (defined(__MIPSEL__) && !defined(__MIPSEB__)) + +#define OSC_HOST_LITTLE_ENDIAN 1 +#undef OSC_HOST_BIG_ENDIAN + +#elif (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ + || (defined(__ARMEB__) && !defined(__ARMEL__)) \ + || (defined(__AARCH64EB__) && !defined(__AARCH64EL__)) \ + || (defined(_MIPSEB) && !defined(_MIPSEL)) \ + || (defined(__MIPSEB) && !defined(__MIPSEL)) \ + || (defined(__MIPSEB__) && !defined(__MIPSEL__)) + +#define OSC_HOST_BIG_ENDIAN 1 +#undef OSC_HOST_LITTLE_ENDIAN + +#endif + +#endif + +#if !defined(OSC_HOST_LITTLE_ENDIAN) && !defined(OSC_HOST_BIG_ENDIAN) + +#error please edit OSCHostEndianness.h or define one of {OSC_HOST_LITTLE_ENDIAN, OSC_HOST_BIG_ENDIAN} to configure endianness + +#endif + +#endif /* INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H */ + diff --git a/OSC/Source/OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.cpp b/OSC/Source/OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.cpp new file mode 100644 index 0000000..58234ba --- /dev/null +++ b/OSC/Source/OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.cpp @@ -0,0 +1,686 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ + +#include "OscPrivatePCH.h" + +#include "OscOutboundPacketStream.h" + +#if defined(__WIN32__) || defined(WIN32) || defined(_WIN32) +#include // for alloca +#else +//#include // alloca on Linux (also OSX) +#include // alloca on OSX and FreeBSD (and Linux?) +#endif + +#include +#include // memcpy, memmove, strcpy, strlen +#include // ptrdiff_t + +#include "OscHostEndianness.h" + +#if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug +namespace std { +using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'. +} +#endif + +namespace osc{ + +static void FromInt32( char *p, int32 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.i = x; + + p[3] = u.c[0]; + p[2] = u.c[1]; + p[1] = u.c[2]; + p[0] = u.c[3]; +#else + *reinterpret_cast(p) = x; +#endif +} + + +static void FromUInt32( char *p, uint32 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint32 i; + char c[4]; + } u; + + u.i = x; + + p[3] = u.c[0]; + p[2] = u.c[1]; + p[1] = u.c[2]; + p[0] = u.c[3]; +#else + *reinterpret_cast(p) = x; +#endif +} + + +static void FromInt64( char *p, int64 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int64 i; + char c[8]; + } u; + + u.i = x; + + p[7] = u.c[0]; + p[6] = u.c[1]; + p[5] = u.c[2]; + p[4] = u.c[3]; + p[3] = u.c[4]; + p[2] = u.c[5]; + p[1] = u.c[6]; + p[0] = u.c[7]; +#else + *reinterpret_cast(p) = x; +#endif +} + + +static void FromUInt64( char *p, uint64 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint64 i; + char c[8]; + } u; + + u.i = x; + + p[7] = u.c[0]; + p[6] = u.c[1]; + p[5] = u.c[2]; + p[4] = u.c[3]; + p[3] = u.c[4]; + p[2] = u.c[5]; + p[1] = u.c[6]; + p[0] = u.c[7]; +#else + *reinterpret_cast(p) = x; +#endif +} + + +// round up to the next highest multiple of 4. unless x is already a multiple of 4 +static inline std::size_t RoundUp4( std::size_t x ) +{ + return (x + 3) & ~((std::size_t)0x03); +} + + +OutboundPacketStream::OutboundPacketStream( char *buffer, std::size_t capacity ) + : data_( buffer ) + , end_( data_ + capacity ) + , typeTagsCurrent_( end_ ) + , messageCursor_( data_ ) + , argumentCurrent_( data_ ) + , elementSizePtr_( 0 ) + , messageIsInProgress_( false ) +{ + // sanity check integer types declared in OscTypes.h + // you'll need to fix OscTypes.h if any of these asserts fail + assert( sizeof(osc::int32) == 4 ); + assert( sizeof(osc::uint32) == 4 ); + assert( sizeof(osc::int64) == 8 ); + assert( sizeof(osc::uint64) == 8 ); +} + + +OutboundPacketStream::~OutboundPacketStream() +{ + +} + + +char *OutboundPacketStream::BeginElement( char *beginPtr ) +{ + if( elementSizePtr_ == 0 ){ + + elementSizePtr_ = reinterpret_cast(data_); + + return beginPtr; + + }else{ + // store an offset to the old element size ptr in the element size slot + // we store an offset rather than the actual pointer to be 64 bit clean. + *reinterpret_cast(beginPtr) = + (uint32)(reinterpret_cast(elementSizePtr_) - data_); + + elementSizePtr_ = reinterpret_cast(beginPtr); + + return beginPtr + 4; + } +} + + +void OutboundPacketStream::EndElement( char *endPtr ) +{ + assert( elementSizePtr_ != 0 ); + + if( elementSizePtr_ == reinterpret_cast(data_) ){ + + elementSizePtr_ = 0; + + }else{ + // while building an element, an offset to the containing element's + // size slot is stored in the elements size slot (or a ptr to data_ + // if there is no containing element). We retrieve that here + uint32 *previousElementSizePtr = + reinterpret_cast(data_ + *elementSizePtr_); + + // then we store the element size in the slot. note that the element + // size does not include the size slot, hence the - 4 below. + + std::ptrdiff_t d = endPtr - reinterpret_cast(elementSizePtr_); + // assert( d >= 4 && d <= 0x7FFFFFFF ); // assume packets smaller than 2Gb + + uint32 elementSize = static_cast(d - 4); + FromUInt32( reinterpret_cast(elementSizePtr_), elementSize ); + + // finally, we reset the element size ptr to the containing element + elementSizePtr_ = previousElementSizePtr; + } +} + + +bool OutboundPacketStream::ElementSizeSlotRequired() const +{ + return (elementSizePtr_ != 0); +} + + +void OutboundPacketStream::CheckForAvailableBundleSpace() +{ + std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) + 16; + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern ) +{ + // plus 4 for at least four bytes of type tag + std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) + + RoundUp4(std::strlen(addressPattern) + 1) + 4; + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::CheckForAvailableArgumentSpace( std::size_t argumentLength ) +{ + // plus three for extra type tag, comma and null terminator + std::size_t required = (argumentCurrent_ - data_) + argumentLength + + RoundUp4( (end_ - typeTagsCurrent_) + 3 ); + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::Clear() +{ + typeTagsCurrent_ = end_; + messageCursor_ = data_; + argumentCurrent_ = data_; + elementSizePtr_ = 0; + messageIsInProgress_ = false; +} + + +std::size_t OutboundPacketStream::Capacity() const +{ + return end_ - data_; +} + + +std::size_t OutboundPacketStream::Size() const +{ + std::size_t result = argumentCurrent_ - data_; + if( IsMessageInProgress() ){ + // account for the length of the type tag string. the total type tag + // includes an initial comma, plus at least one terminating \0 + result += RoundUp4( (end_ - typeTagsCurrent_) + 2 ); + } + + return result; +} + + +const char *OutboundPacketStream::Data() const +{ + return data_; +} + + +bool OutboundPacketStream::IsReady() const +{ + return (!IsMessageInProgress() && !IsBundleInProgress()); +} + + +bool OutboundPacketStream::IsMessageInProgress() const +{ + return messageIsInProgress_; +} + + +bool OutboundPacketStream::IsBundleInProgress() const +{ + return (elementSizePtr_ != 0); +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs ) +{ + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + CheckForAvailableBundleSpace(); + + messageCursor_ = BeginElement( messageCursor_ ); + + std::memcpy( messageCursor_, "#bundle\0", 8 ); + FromUInt64( messageCursor_ + 8, rhs.timeTag ); + + messageCursor_ += 16; + argumentCurrent_ = messageCursor_; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs ) +{ + (void) rhs; + + if( !IsBundleInProgress() ) + throw BundleNotInProgressException(); + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + EndElement( messageCursor_ ); + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs ) +{ + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + CheckForAvailableMessageSpace( rhs.addressPattern ); + + messageCursor_ = BeginElement( messageCursor_ ); + + std::strcpy( messageCursor_, rhs.addressPattern ); + std::size_t rhsLength = std::strlen(rhs.addressPattern); + messageCursor_ += rhsLength + 1; + + // zero pad to 4-byte boundary + std::size_t i = rhsLength + 1; + while( i & 0x3 ){ + *messageCursor_++ = '\0'; + ++i; + } + + argumentCurrent_ = messageCursor_; + typeTagsCurrent_ = end_; + + messageIsInProgress_ = true; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs ) +{ + (void) rhs; + + if( !IsMessageInProgress() ) + throw MessageNotInProgressException(); + + std::size_t typeTagsCount = end_ - typeTagsCurrent_; + + if( typeTagsCount ){ + + char *tempTypeTags = (char*)alloca(typeTagsCount); + std::memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount ); + + // slot size includes comma and null terminator + std::size_t typeTagSlotSize = RoundUp4( typeTagsCount + 2 ); + + std::size_t argumentsSize = argumentCurrent_ - messageCursor_; + + std::memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize ); + + messageCursor_[0] = ','; + // copy type tags in reverse (really forward) order + for( std::size_t i=0; i < typeTagsCount; ++i ) + messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ]; + + char *p = messageCursor_ + 1 + typeTagsCount; + for( std::size_t i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i ) + *p++ = '\0'; + + typeTagsCurrent_ = end_; + + // advance messageCursor_ for next message + messageCursor_ += typeTagSlotSize + argumentsSize; + + }else{ + // send an empty type tags string + std::memcpy( messageCursor_, ",\0\0\0", 4 ); + + // advance messageCursor_ for next message + messageCursor_ += 4; + } + + argumentCurrent_ = messageCursor_; + + EndElement( messageCursor_ ); + + messageIsInProgress_ = false; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs ) +{ + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG); + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs ) +{ + (void) rhs; + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = NIL_TYPE_TAG; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs ) +{ + (void) rhs; + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = INT32_TYPE_TAG; + FromInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( float rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = FLOAT_TYPE_TAG; + +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + float f; + char c[4]; + } u; + + u.f = rhs; + + argumentCurrent_[3] = u.c[0]; + argumentCurrent_[2] = u.c[1]; + argumentCurrent_[1] = u.c[2]; + argumentCurrent_[0] = u.c[3]; +#else + *reinterpret_cast(argumentCurrent_) = rhs; +#endif + + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( char rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = CHAR_TYPE_TAG; + FromInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = INT64_TYPE_TAG; + FromInt64( argumentCurrent_, rhs ); + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG; + FromUInt64( argumentCurrent_, rhs ); + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( double rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG; + +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + double f; + char c[8]; + } u; + + u.f = rhs; + + argumentCurrent_[7] = u.c[0]; + argumentCurrent_[6] = u.c[1]; + argumentCurrent_[5] = u.c[2]; + argumentCurrent_[4] = u.c[3]; + argumentCurrent_[3] = u.c[4]; + argumentCurrent_[2] = u.c[5]; + argumentCurrent_[1] = u.c[6]; + argumentCurrent_[0] = u.c[7]; +#else + *reinterpret_cast(argumentCurrent_) = rhs; +#endif + + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs ) +{ + CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) ); + + *(--typeTagsCurrent_) = STRING_TYPE_TAG; + std::strcpy( argumentCurrent_, rhs ); + std::size_t rhsLength = std::strlen(rhs); + argumentCurrent_ += rhsLength + 1; + + // zero pad to 4-byte boundary + std::size_t i = rhsLength + 1; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs ) +{ + CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) ); + + *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG; + std::strcpy( argumentCurrent_, rhs ); + std::size_t rhsLength = std::strlen(rhs); + argumentCurrent_ += rhsLength + 1; + + // zero pad to 4-byte boundary + std::size_t i = rhsLength + 1; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs ) +{ + CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) ); + + *(--typeTagsCurrent_) = BLOB_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs.size ); + argumentCurrent_ += 4; + + std::memcpy( argumentCurrent_, rhs.data, rhs.size ); + argumentCurrent_ += rhs.size; + + // zero pad to 4-byte boundary + unsigned long i = rhs.size; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + +OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayInitiator& rhs ) +{ + (void) rhs; + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = ARRAY_BEGIN_TYPE_TAG; + + return *this; +} + +OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayTerminator& rhs ) +{ + (void) rhs; + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = ARRAY_END_TYPE_TAG; + + return *this; +} + +} // namespace osc + + diff --git a/OSC/Source/OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.h b/OSC/Source/OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.h new file mode 100644 index 0000000..cff16f9 --- /dev/null +++ b/OSC/Source/OSC/Private/Common/oscpack/osc/OscOutboundPacketStream.h @@ -0,0 +1,154 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ +#ifndef INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H +#define INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H + +#include // size_t + +#include "OscTypes.h" +#include "OscException.h" + + +namespace osc{ + +class OutOfBufferMemoryException : public Exception{ +public: + OutOfBufferMemoryException( const char *w="out of buffer memory" ) + : Exception( w ) {} +}; + +class BundleNotInProgressException : public Exception{ +public: + BundleNotInProgressException( + const char *w="call to EndBundle when bundle is not in progress" ) + : Exception( w ) {} +}; + +class MessageInProgressException : public Exception{ +public: + MessageInProgressException( + const char *w="opening or closing bundle or message while message is in progress" ) + : Exception( w ) {} +}; + +class MessageNotInProgressException : public Exception{ +public: + MessageNotInProgressException( + const char *w="call to EndMessage when message is not in progress" ) + : Exception( w ) {} +}; + + +class OutboundPacketStream{ +public: + OutboundPacketStream( char *buffer, std::size_t capacity ); + ~OutboundPacketStream(); + + void Clear(); + + std::size_t Capacity() const; + + // invariant: size() is valid even while building a message. + std::size_t Size() const; + + const char *Data() const; + + // indicates that all messages have been closed with a matching EndMessage + // and all bundles have been closed with a matching EndBundle + bool IsReady() const; + + bool IsMessageInProgress() const; + bool IsBundleInProgress() const; + + OutboundPacketStream& operator<<( const BundleInitiator& rhs ); + OutboundPacketStream& operator<<( const BundleTerminator& rhs ); + + OutboundPacketStream& operator<<( const BeginMessage& rhs ); + OutboundPacketStream& operator<<( const MessageTerminator& rhs ); + + OutboundPacketStream& operator<<( bool rhs ); + OutboundPacketStream& operator<<( const NilType& rhs ); + OutboundPacketStream& operator<<( const InfinitumType& rhs ); + OutboundPacketStream& operator<<( int32 rhs ); + +#if !(defined(__x86_64__) || defined(_M_X64)) + OutboundPacketStream& operator<<( int rhs ) + { *this << (int32)rhs; return *this; } +#endif + + OutboundPacketStream& operator<<( float rhs ); + OutboundPacketStream& operator<<( char rhs ); + OutboundPacketStream& operator<<( const RgbaColor& rhs ); + OutboundPacketStream& operator<<( const MidiMessage& rhs ); + OutboundPacketStream& operator<<( int64 rhs ); + OutboundPacketStream& operator<<( const TimeTag& rhs ); + OutboundPacketStream& operator<<( double rhs ); + OutboundPacketStream& operator<<( const char* rhs ); + OutboundPacketStream& operator<<( const Symbol& rhs ); + OutboundPacketStream& operator<<( const Blob& rhs ); + + OutboundPacketStream& operator<<( const ArrayInitiator& rhs ); + OutboundPacketStream& operator<<( const ArrayTerminator& rhs ); + +private: + + char *BeginElement( char *beginPtr ); + void EndElement( char *endPtr ); + + bool ElementSizeSlotRequired() const; + void CheckForAvailableBundleSpace(); + void CheckForAvailableMessageSpace( const char *addressPattern ); + void CheckForAvailableArgumentSpace( std::size_t argumentLength ); + + char *data_; + char *end_; + + char *typeTagsCurrent_; // stored in reverse order + char *messageCursor_; + char *argumentCurrent_; + + // elementSizePtr_ has two special values: 0 indicates that a bundle + // isn't open, and elementSizePtr_==data_ indicates that a bundle is + // open but that it doesn't have a size slot (ie the outermost bundle) + uint32 *elementSizePtr_; + + bool messageIsInProgress_; +}; + +} // namespace osc + +#endif /* INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H */ diff --git a/OSC/Source/OSC/Private/Common/oscpack/osc/OscReceivedElements.cpp b/OSC/Source/OSC/Private/Common/oscpack/osc/OscReceivedElements.cpp new file mode 100644 index 0000000..2af395a --- /dev/null +++ b/OSC/Source/OSC/Private/Common/oscpack/osc/OscReceivedElements.cpp @@ -0,0 +1,799 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ + +#include "OscPrivatePCH.h" + +#include "OscReceivedElements.h" + +#include "OscHostEndianness.h" + +#include // ptrdiff_t + +namespace osc{ + + +// return the first 4 byte boundary after the end of a str4 +// be careful about calling this version if you don't know whether +// the string is terminated correctly. +static inline const char* FindStr4End( const char *p ) +{ + if( p[0] == '\0' ) // special case for SuperCollider integer address pattern + return p + 4; + + p += 3; + + while( *p ) + p += 4; + + return p + 1; +} + + +// return the first 4 byte boundary after the end of a str4 +// returns 0 if p == end or if the string is unterminated +static inline const char* FindStr4End( const char *p, const char *end ) +{ + if( p >= end ) + return 0; + + if( p[0] == '\0' ) // special case for SuperCollider integer address pattern + return p + 4; + + p += 3; + end -= 1; + + while( p < end && *p ) + p += 4; + + if( *p ) + return 0; + else + return p + 1; +} + + +// round up to the next highest multiple of 4. unless x is already a multiple of 4 +static inline uint32 RoundUp4( uint32 x ) +{ + return (x + 3) & ~((uint32)0x03); +} + + +static inline int32 ToInt32( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.c[0] = p[3]; + u.c[1] = p[2]; + u.c[2] = p[1]; + u.c[3] = p[0]; + + return u.i; +#else + return *(int32*)p; +#endif +} + + +static inline uint32 ToUInt32( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint32 i; + char c[4]; + } u; + + u.c[0] = p[3]; + u.c[1] = p[2]; + u.c[2] = p[1]; + u.c[3] = p[0]; + + return u.i; +#else + return *(uint32*)p; +#endif +} + + +static inline int64 ToInt64( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int64 i; + char c[8]; + } u; + + u.c[0] = p[7]; + u.c[1] = p[6]; + u.c[2] = p[5]; + u.c[3] = p[4]; + u.c[4] = p[3]; + u.c[5] = p[2]; + u.c[6] = p[1]; + u.c[7] = p[0]; + + return u.i; +#else + return *(int64*)p; +#endif +} + + +static inline uint64 ToUInt64( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint64 i; + char c[8]; + } u; + + u.c[0] = p[7]; + u.c[1] = p[6]; + u.c[2] = p[5]; + u.c[3] = p[4]; + u.c[4] = p[3]; + u.c[5] = p[2]; + u.c[6] = p[1]; + u.c[7] = p[0]; + + return u.i; +#else + return *(uint64*)p; +#endif +} + +//------------------------------------------------------------------------------ + +bool ReceivedPacket::IsBundle() const +{ + return (Size() > 0 && Contents()[0] == '#'); +} + +//------------------------------------------------------------------------------ + +bool ReceivedBundleElement::IsBundle() const +{ + return (Size() > 0 && Contents()[0] == '#'); +} + + +osc_bundle_element_size_t ReceivedBundleElement::Size() const +{ + return ToInt32( sizePtr_ ); +} + +//------------------------------------------------------------------------------ + +bool ReceivedMessageArgument::AsBool() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == TRUE_TYPE_TAG ) + return true; + else if( *typeTagPtr_ == FALSE_TYPE_TAG ) + return false; + else + throw WrongArgumentTypeException(); +} + + +bool ReceivedMessageArgument::AsBoolUnchecked() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == TRUE_TYPE_TAG ) + return true; + else + return false; +} + + +int32 ReceivedMessageArgument::AsInt32() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == INT32_TYPE_TAG ) + return AsInt32Unchecked(); + else + throw WrongArgumentTypeException(); +} + + +int32 ReceivedMessageArgument::AsInt32Unchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.c[0] = argumentPtr_[3]; + u.c[1] = argumentPtr_[2]; + u.c[2] = argumentPtr_[1]; + u.c[3] = argumentPtr_[0]; + + return u.i; +#else + return *(int32*)argument_; +#endif +} + + +float ReceivedMessageArgument::AsFloat() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == FLOAT_TYPE_TAG ) + return AsFloatUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +float ReceivedMessageArgument::AsFloatUnchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + float f; + char c[4]; + } u; + + u.c[0] = argumentPtr_[3]; + u.c[1] = argumentPtr_[2]; + u.c[2] = argumentPtr_[1]; + u.c[3] = argumentPtr_[0]; + + return u.f; +#else + return *(float*)argument_; +#endif +} + + +char ReceivedMessageArgument::AsChar() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == CHAR_TYPE_TAG ) + return AsCharUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +char ReceivedMessageArgument::AsCharUnchecked() const +{ + return (char)ToInt32( argumentPtr_ ); +} + + +uint32 ReceivedMessageArgument::AsRgbaColor() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == RGBA_COLOR_TYPE_TAG ) + return AsRgbaColorUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const +{ + return ToUInt32( argumentPtr_ ); +} + + +uint32 ReceivedMessageArgument::AsMidiMessage() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG ) + return AsMidiMessageUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const +{ + return ToUInt32( argumentPtr_ ); +} + + +int64 ReceivedMessageArgument::AsInt64() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == INT64_TYPE_TAG ) + return AsInt64Unchecked(); + else + throw WrongArgumentTypeException(); +} + + +int64 ReceivedMessageArgument::AsInt64Unchecked() const +{ + return ToInt64( argumentPtr_ ); +} + + +uint64 ReceivedMessageArgument::AsTimeTag() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == TIME_TAG_TYPE_TAG ) + return AsTimeTagUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const +{ + return ToUInt64( argumentPtr_ ); +} + + +double ReceivedMessageArgument::AsDouble() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == DOUBLE_TYPE_TAG ) + return AsDoubleUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +double ReceivedMessageArgument::AsDoubleUnchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + double d; + char c[8]; + } u; + + u.c[0] = argumentPtr_[7]; + u.c[1] = argumentPtr_[6]; + u.c[2] = argumentPtr_[5]; + u.c[3] = argumentPtr_[4]; + u.c[4] = argumentPtr_[3]; + u.c[5] = argumentPtr_[2]; + u.c[6] = argumentPtr_[1]; + u.c[7] = argumentPtr_[0]; + + return u.d; +#else + return *(double*)argument_; +#endif +} + + +const char* ReceivedMessageArgument::AsString() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == STRING_TYPE_TAG ) + return argumentPtr_; + else + throw WrongArgumentTypeException(); +} + + +const char* ReceivedMessageArgument::AsSymbol() const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == SYMBOL_TYPE_TAG ) + return argumentPtr_; + else + throw WrongArgumentTypeException(); +} + + +void ReceivedMessageArgument::AsBlob( const void*& data, osc_bundle_element_size_t& size ) const +{ + if( !typeTagPtr_ ) + throw MissingArgumentException(); + else if( *typeTagPtr_ == BLOB_TYPE_TAG ) + AsBlobUnchecked( data, size ); + else + throw WrongArgumentTypeException(); +} + + +void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const +{ + // read blob size as an unsigned int then validate + osc_bundle_element_size_t sizeResult = (osc_bundle_element_size_t)ToUInt32( argumentPtr_ ); + if( !IsValidElementSizeValue(sizeResult) ) + throw MalformedMessageException("invalid blob size"); + + size = sizeResult; + data = (void*)(argumentPtr_+ osc::OSC_SIZEOF_INT32); +} + +std::size_t ReceivedMessageArgument::ComputeArrayItemCount() const +{ + // it is only valid to call ComputeArrayItemCount when the argument is the array start marker + if( !IsArrayBegin() ) + throw WrongArgumentTypeException(); + + std::size_t result = 0; + unsigned int level = 0; + const char *typeTag = typeTagPtr_ + 1; + + // iterate through all type tags. note that ReceivedMessage::Init + // has already checked that the message is well formed. + while( *typeTag ) { + switch( *typeTag++ ) { + case ARRAY_BEGIN_TYPE_TAG: + level += 1; + break; + + case ARRAY_END_TYPE_TAG: + if(level == 0) + return result; + level -= 1; + break; + + default: + if( level == 0 ) // only count items at level 0 + ++result; + } + } + + return result; +} + +//------------------------------------------------------------------------------ + +void ReceivedMessageArgumentIterator::Advance() +{ + if( !value_.typeTagPtr_ ) + return; + + switch( *value_.typeTagPtr_++ ){ + case '\0': + // don't advance past end + --value_.typeTagPtr_; + break; + + case TRUE_TYPE_TAG: + case FALSE_TYPE_TAG: + case NIL_TYPE_TAG: + case INFINITUM_TYPE_TAG: + + // zero length + break; + + case INT32_TYPE_TAG: + case FLOAT_TYPE_TAG: + case CHAR_TYPE_TAG: + case RGBA_COLOR_TYPE_TAG: + case MIDI_MESSAGE_TYPE_TAG: + + value_.argumentPtr_ += 4; + break; + + case INT64_TYPE_TAG: + case TIME_TAG_TYPE_TAG: + case DOUBLE_TYPE_TAG: + + value_.argumentPtr_ += 8; + break; + + case STRING_TYPE_TAG: + case SYMBOL_TYPE_TAG: + + // we use the unsafe function FindStr4End(char*) here because all of + // the arguments have already been validated in + // ReceivedMessage::Init() below. + + value_.argumentPtr_ = FindStr4End( value_.argumentPtr_ ); + break; + + case BLOB_TYPE_TAG: + { + // treat blob size as an unsigned int for the purposes of this calculation + uint32 blobSize = ToUInt32( value_.argumentPtr_ ); + value_.argumentPtr_ = value_.argumentPtr_ + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize ); + } + break; + + case ARRAY_BEGIN_TYPE_TAG: + case ARRAY_END_TYPE_TAG: + + // [ Indicates the beginning of an array. The tags following are for + // data in the Array until a close brace tag is reached. + // ] Indicates the end of an array. + + // zero length, don't advance argument ptr + break; + + default: // unknown type tag + // don't advance + --value_.typeTagPtr_; + break; + } +} + +//------------------------------------------------------------------------------ + +ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet ) + : addressPattern_( packet.Contents() ) +{ + Init( packet.Contents(), packet.Size() ); +} + + +ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement ) + : addressPattern_( bundleElement.Contents() ) +{ + Init( bundleElement.Contents(), bundleElement.Size() ); +} + + +bool ReceivedMessage::AddressPatternIsUInt32() const +{ + return (addressPattern_[0] == '\0'); +} + + +uint32 ReceivedMessage::AddressPatternAsUInt32() const +{ + return ToUInt32( addressPattern_ ); +} + + +void ReceivedMessage::Init( const char *message, osc_bundle_element_size_t size ) +{ + if( !IsValidElementSizeValue(size) ) + throw MalformedMessageException( "invalid message size" ); + + if( size == 0 ) + throw MalformedMessageException( "zero length messages not permitted" ); + + if( !IsMultipleOf4(size) ) + throw MalformedMessageException( "message size must be multiple of four" ); + + const char *end = message + size; + + typeTagsBegin_ = FindStr4End( addressPattern_, end ); + if( typeTagsBegin_ == 0 ){ + // address pattern was not terminated before end + throw MalformedMessageException( "unterminated address pattern" ); + } + + if( typeTagsBegin_ == end ){ + // message consists of only the address pattern - no arguments or type tags. + typeTagsBegin_ = 0; + typeTagsEnd_ = 0; + arguments_ = 0; + + }else{ + if( *typeTagsBegin_ != ',' ) + throw MalformedMessageException( "type tags not present" ); + + if( *(typeTagsBegin_ + 1) == '\0' ){ + // zero length type tags + typeTagsBegin_ = 0; + typeTagsEnd_ = 0; + arguments_ = 0; + + }else{ + // check that all arguments are present and well formed + + arguments_ = FindStr4End( typeTagsBegin_, end ); + if( arguments_ == 0 ){ + throw MalformedMessageException( "type tags were not terminated before end of message" ); + } + + ++typeTagsBegin_; // advance past initial ',' + + const char *typeTag = typeTagsBegin_; + const char *argument = arguments_; + unsigned int arrayLevel = 0; + + do{ + switch( *typeTag ){ + case TRUE_TYPE_TAG: + case FALSE_TYPE_TAG: + case NIL_TYPE_TAG: + case INFINITUM_TYPE_TAG: + // zero length + break; + + // [ Indicates the beginning of an array. The tags following are for + // data in the Array until a close brace tag is reached. + // ] Indicates the end of an array. + case ARRAY_BEGIN_TYPE_TAG: + ++arrayLevel; + // (zero length argument data) + break; + + case ARRAY_END_TYPE_TAG: + --arrayLevel; + // (zero length argument data) + break; + + case INT32_TYPE_TAG: + case FLOAT_TYPE_TAG: + case CHAR_TYPE_TAG: + case RGBA_COLOR_TYPE_TAG: + case MIDI_MESSAGE_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument += 4; + if( argument > end ) + throw MalformedMessageException( "arguments exceed message size" ); + break; + + case INT64_TYPE_TAG: + case TIME_TAG_TYPE_TAG: + case DOUBLE_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument += 8; + if( argument > end ) + throw MalformedMessageException( "arguments exceed message size" ); + break; + + case STRING_TYPE_TAG: + case SYMBOL_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument = FindStr4End( argument, end ); + if( argument == 0 ) + throw MalformedMessageException( "unterminated string argument" ); + break; + + case BLOB_TYPE_TAG: + { + if( argument + osc::OSC_SIZEOF_INT32 > end ) + MalformedMessageException( "arguments exceed message size" ); + + // treat blob size as an unsigned int for the purposes of this calculation + uint32 blobSize = ToUInt32( argument ); + argument = argument + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize ); + if( argument > end ) + MalformedMessageException( "arguments exceed message size" ); + } + break; + + default: + throw MalformedMessageException( "unknown type tag" ); + } + + }while( *++typeTag != '\0' ); + typeTagsEnd_ = typeTag; + + if( arrayLevel != 0 ) + throw MalformedMessageException( "array was not terminated before end of message (expected ']' end of array tag)" ); + } + + // These invariants should be guaranteed by the above code. + // we depend on them in the implementation of ArgumentCount() +#ifndef NDEBUG + std::ptrdiff_t argumentCount = typeTagsEnd_ - typeTagsBegin_; + assert( argumentCount >= 0 ); + assert( argumentCount <= OSC_INT32_MAX ); +#endif + } +} + +//------------------------------------------------------------------------------ + +ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet ) + : elementCount_( 0 ) +{ + Init( packet.Contents(), packet.Size() ); +} + + +ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement ) + : elementCount_( 0 ) +{ + Init( bundleElement.Contents(), bundleElement.Size() ); +} + + +void ReceivedBundle::Init( const char *bundle, osc_bundle_element_size_t size ) +{ + + if( !IsValidElementSizeValue(size) ) + throw MalformedBundleException( "invalid bundle size" ); + + if( size < 16 ) + throw MalformedBundleException( "packet too short for bundle" ); + + if( !IsMultipleOf4(size) ) + throw MalformedBundleException( "bundle size must be multiple of four" ); + + if( bundle[0] != '#' + || bundle[1] != 'b' + || bundle[2] != 'u' + || bundle[3] != 'n' + || bundle[4] != 'd' + || bundle[5] != 'l' + || bundle[6] != 'e' + || bundle[7] != '\0' ) + throw MalformedBundleException( "bad bundle address pattern" ); + + end_ = bundle + size; + + timeTag_ = bundle + 8; + + const char *p = timeTag_ + 8; + + while( p < end_ ){ + if( p + osc::OSC_SIZEOF_INT32 > end_ ) + throw MalformedBundleException( "packet too short for elementSize" ); + + // treat element size as an unsigned int for the purposes of this calculation + uint32 elementSize = ToUInt32( p ); + if( (elementSize & ((uint32)0x03)) != 0 ) + throw MalformedBundleException( "bundle element size must be multiple of four" ); + + p += osc::OSC_SIZEOF_INT32 + elementSize; + if( p > end_ ) + throw MalformedBundleException( "packet too short for bundle element" ); + + ++elementCount_; + } + + if( p != end_ ) + throw MalformedBundleException( "bundle contents " ); +} + + +uint64 ReceivedBundle::TimeTag() const +{ + return ToUInt64( timeTag_ ); +} + + +} // namespace osc + diff --git a/OSC/Source/OSC/Private/Common/oscpack/osc/OscReceivedElements.h b/OSC/Source/OSC/Private/Common/oscpack/osc/OscReceivedElements.h new file mode 100644 index 0000000..f8b0c0d --- /dev/null +++ b/OSC/Source/OSC/Private/Common/oscpack/osc/OscReceivedElements.h @@ -0,0 +1,548 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ +#ifndef INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H +#define INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H + +#include +#include +#include // size_t + +#include "OscTypes.h" +#include "OscException.h" + + +namespace osc{ + + +class MalformedPacketException : public Exception{ +public: + MalformedPacketException( const char *w="malformed packet" ) + : Exception( w ) {} +}; + +class MalformedMessageException : public Exception{ +public: + MalformedMessageException( const char *w="malformed message" ) + : Exception( w ) {} +}; + +class MalformedBundleException : public Exception{ +public: + MalformedBundleException( const char *w="malformed bundle" ) + : Exception( w ) {} +}; + +class WrongArgumentTypeException : public Exception{ +public: + WrongArgumentTypeException( const char *w="wrong argument type" ) + : Exception( w ) {} +}; + +class MissingArgumentException : public Exception{ +public: + MissingArgumentException( const char *w="missing argument" ) + : Exception( w ) {} +}; + +class ExcessArgumentException : public Exception{ +public: + ExcessArgumentException( const char *w="too many arguments" ) + : Exception( w ) {} +}; + + +class ReceivedPacket{ +public: + // Although the OSC spec is not entirely clear on this, we only support + // packets up to 0x7FFFFFFC bytes long (the maximum 4-byte aligned value + // representable by an int32). An exception will be raised if you pass a + // larger value to the ReceivedPacket() constructor. + + ReceivedPacket( const char *contents, osc_bundle_element_size_t size ) + : contents_( contents ) + , size_( ValidateSize(size) ) {} + + ReceivedPacket( const char *contents, std::size_t size ) + : contents_( contents ) + , size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {} + +#if !(defined(__x86_64__) || defined(_M_X64)) + ReceivedPacket( const char *contents, int size ) + : contents_( contents ) + , size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {} +#endif + + bool IsMessage() const { return !IsBundle(); } + bool IsBundle() const; + + osc_bundle_element_size_t Size() const { return size_; } + const char *Contents() const { return contents_; } + +private: + const char *contents_; + osc_bundle_element_size_t size_; + + static osc_bundle_element_size_t ValidateSize( osc_bundle_element_size_t size ) + { + // sanity check integer types declared in OscTypes.h + // you'll need to fix OscTypes.h if any of these asserts fail + assert( sizeof(osc::int32) == 4 ); + assert( sizeof(osc::uint32) == 4 ); + assert( sizeof(osc::int64) == 8 ); + assert( sizeof(osc::uint64) == 8 ); + + if( !IsValidElementSizeValue(size) ) + throw MalformedPacketException( "invalid packet size" ); + + if( size == 0 ) + throw MalformedPacketException( "zero length elements not permitted" ); + + if( !IsMultipleOf4(size) ) + throw MalformedPacketException( "element size must be multiple of four" ); + + return size; + } +}; + + +class ReceivedBundleElement{ +public: + ReceivedBundleElement( const char *sizePtr ) + : sizePtr_( sizePtr ) {} + + friend class ReceivedBundleElementIterator; + + bool IsMessage() const { return !IsBundle(); } + bool IsBundle() const; + + osc_bundle_element_size_t Size() const; + const char *Contents() const { return sizePtr_ + osc::OSC_SIZEOF_INT32; } + +private: + const char *sizePtr_; +}; + + +class ReceivedBundleElementIterator{ +public: + ReceivedBundleElementIterator( const char *sizePtr ) + : value_( sizePtr ) {} + + ReceivedBundleElementIterator operator++() + { + Advance(); + return *this; + } + + ReceivedBundleElementIterator operator++(int) + { + ReceivedBundleElementIterator old( *this ); + Advance(); + return old; + } + + const ReceivedBundleElement& operator*() const { return value_; } + + const ReceivedBundleElement* operator->() const { return &value_; } + + friend bool operator==(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ); + +private: + ReceivedBundleElement value_; + + void Advance() { value_.sizePtr_ = value_.Contents() + value_.Size(); } + + bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const + { + return value_.sizePtr_ == rhs.value_.sizePtr_; + } +}; + +inline bool operator==(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ) +{ + return lhs.IsEqualTo( rhs ); +} + +inline bool operator!=(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ) +{ + return !( lhs == rhs ); +} + + +class ReceivedMessageArgument{ +public: + ReceivedMessageArgument( const char *typeTagPtr, const char *argumentPtr ) + : typeTagPtr_( typeTagPtr ) + , argumentPtr_( argumentPtr ) {} + + friend class ReceivedMessageArgumentIterator; + + char TypeTag() const { return *typeTagPtr_; } + + // the unchecked methods below don't check whether the argument actually + // is of the specified type. they should only be used if you've already + // checked the type tag or the associated IsType() method. + + bool IsBool() const + { return *typeTagPtr_ == TRUE_TYPE_TAG || *typeTagPtr_ == FALSE_TYPE_TAG; } + bool AsBool() const; + bool AsBoolUnchecked() const; + + bool IsNil() const { return *typeTagPtr_ == NIL_TYPE_TAG; } + bool IsInfinitum() const { return *typeTagPtr_ == INFINITUM_TYPE_TAG; } + + bool IsInt32() const { return *typeTagPtr_ == INT32_TYPE_TAG; } + int32 AsInt32() const; + int32 AsInt32Unchecked() const; + + bool IsFloat() const { return *typeTagPtr_ == FLOAT_TYPE_TAG; } + float AsFloat() const; + float AsFloatUnchecked() const; + + bool IsChar() const { return *typeTagPtr_ == CHAR_TYPE_TAG; } + char AsChar() const; + char AsCharUnchecked() const; + + bool IsRgbaColor() const { return *typeTagPtr_ == RGBA_COLOR_TYPE_TAG; } + uint32 AsRgbaColor() const; + uint32 AsRgbaColorUnchecked() const; + + bool IsMidiMessage() const { return *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG; } + uint32 AsMidiMessage() const; + uint32 AsMidiMessageUnchecked() const; + + bool IsInt64() const { return *typeTagPtr_ == INT64_TYPE_TAG; } + int64 AsInt64() const; + int64 AsInt64Unchecked() const; + + bool IsTimeTag() const { return *typeTagPtr_ == TIME_TAG_TYPE_TAG; } + uint64 AsTimeTag() const; + uint64 AsTimeTagUnchecked() const; + + bool IsDouble() const { return *typeTagPtr_ == DOUBLE_TYPE_TAG; } + double AsDouble() const; + double AsDoubleUnchecked() const; + + bool IsString() const { return *typeTagPtr_ == STRING_TYPE_TAG; } + const char* AsString() const; + const char* AsStringUnchecked() const { return argumentPtr_; } + + bool IsSymbol() const { return *typeTagPtr_ == SYMBOL_TYPE_TAG; } + const char* AsSymbol() const; + const char* AsSymbolUnchecked() const { return argumentPtr_; } + + bool IsBlob() const { return *typeTagPtr_ == BLOB_TYPE_TAG; } + void AsBlob( const void*& data, osc_bundle_element_size_t& size ) const; + void AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const; + + bool IsArrayBegin() const { return *typeTagPtr_ == ARRAY_BEGIN_TYPE_TAG; } + bool IsArrayEnd() const { return *typeTagPtr_ == ARRAY_END_TYPE_TAG; } + // Calculate the number of top-level items in the array. Nested arrays count as one item. + // Only valid at array start. Will throw an exception if IsArrayStart() == false. + std::size_t ComputeArrayItemCount() const; + +private: + const char *typeTagPtr_; + const char *argumentPtr_; +}; + + +class ReceivedMessageArgumentIterator{ +public: + ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments ) + : value_( typeTags, arguments ) {} + + ReceivedMessageArgumentIterator operator++() + { + Advance(); + return *this; + } + + ReceivedMessageArgumentIterator operator++(int) + { + ReceivedMessageArgumentIterator old( *this ); + Advance(); + return old; + } + + const ReceivedMessageArgument& operator*() const { return value_; } + + const ReceivedMessageArgument* operator->() const { return &value_; } + + friend bool operator==(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ); + +private: + ReceivedMessageArgument value_; + + void Advance(); + + bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const + { + return value_.typeTagPtr_ == rhs.value_.typeTagPtr_; + } +}; + +inline bool operator==(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ) +{ + return lhs.IsEqualTo( rhs ); +} + +inline bool operator!=(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ) +{ + return !( lhs == rhs ); +} + + +class ReceivedMessageArgumentStream{ + friend class ReceivedMessage; + ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin, + const ReceivedMessageArgumentIterator& end ) + : p_( begin ) + , end_( end ) {} + + ReceivedMessageArgumentIterator p_, end_; + +public: + + // end of stream + bool Eos() const { return p_ == end_; } + + ReceivedMessageArgumentStream& operator>>( bool& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsBool(); + return *this; + } + + // not sure if it would be useful to stream Nil and Infinitum + // for now it's not possible + // same goes for array boundaries + + ReceivedMessageArgumentStream& operator>>( int32& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsInt32(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( float& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsFloat(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( char& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsChar(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsRgbaColor(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsMidiMessage(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( int64& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsInt64(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( TimeTag& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsTimeTag(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( double& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsDouble(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( Blob& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + (*p_++).AsBlob( rhs.data, rhs.size ); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( const char*& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsString(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( Symbol& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsSymbol(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs ) + { + (void) rhs; // suppress unused parameter warning + + if( !Eos() ) + throw ExcessArgumentException(); + + return *this; + } +}; + + +class ReceivedMessage{ + void Init( const char *bundle, osc_bundle_element_size_t size ); +public: + explicit ReceivedMessage( const ReceivedPacket& packet ); + explicit ReceivedMessage( const ReceivedBundleElement& bundleElement ); + + const char *AddressPattern() const { return addressPattern_; } + + // Support for non-standard SuperCollider integer address patterns: + bool AddressPatternIsUInt32() const; + uint32 AddressPatternAsUInt32() const; + + uint32 ArgumentCount() const { return static_cast(typeTagsEnd_ - typeTagsBegin_); } + + const char *TypeTags() const { return typeTagsBegin_; } + + + typedef ReceivedMessageArgumentIterator const_iterator; + + ReceivedMessageArgumentIterator ArgumentsBegin() const + { + return ReceivedMessageArgumentIterator( typeTagsBegin_, arguments_ ); + } + + ReceivedMessageArgumentIterator ArgumentsEnd() const + { + return ReceivedMessageArgumentIterator( typeTagsEnd_, 0 ); + } + + ReceivedMessageArgumentStream ArgumentStream() const + { + return ReceivedMessageArgumentStream( ArgumentsBegin(), ArgumentsEnd() ); + } + +private: + const char *addressPattern_; + const char *typeTagsBegin_; + const char *typeTagsEnd_; + const char *arguments_; +}; + + +class ReceivedBundle{ + void Init( const char *message, osc_bundle_element_size_t size ); +public: + explicit ReceivedBundle( const ReceivedPacket& packet ); + explicit ReceivedBundle( const ReceivedBundleElement& bundleElement ); + + uint64 TimeTag() const; + + uint32 ElementCount() const { return elementCount_; } + + typedef ReceivedBundleElementIterator const_iterator; + + ReceivedBundleElementIterator ElementsBegin() const + { + return ReceivedBundleElementIterator( timeTag_ + 8 ); + } + + ReceivedBundleElementIterator ElementsEnd() const + { + return ReceivedBundleElementIterator( end_ ); + } + +private: + const char *timeTag_; + const char *end_; + uint32 elementCount_; +}; + + +} // namespace osc + + +#endif /* INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H */ diff --git a/OSC/Source/OSC/Private/Common/oscpack/osc/OscTypes.cpp b/OSC/Source/OSC/Private/Common/oscpack/osc/OscTypes.cpp new file mode 100644 index 0000000..b096315 --- /dev/null +++ b/OSC/Source/OSC/Private/Common/oscpack/osc/OscTypes.cpp @@ -0,0 +1,55 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ + +#include "OscPrivatePCH.h" + +#include "OscTypes.h" + +namespace osc{ + +BundleInitiator BeginBundleImmediate(1); +BundleTerminator EndBundle; +MessageTerminator EndMessage; +NilType OscNil; +#ifndef _OBJC_OBJC_H_ +NilType Nil; // Objective-C defines Nil. so our Nil is deprecated. use OscNil instead +#endif +InfinitumType Infinitum; +ArrayInitiator BeginArray; +ArrayTerminator EndArray; + +} // namespace osc diff --git a/OSC/Source/OSC/Private/Common/oscpack/osc/OscTypes.h b/OSC/Source/OSC/Private/Common/oscpack/osc/OscTypes.h new file mode 100644 index 0000000..a21f23b --- /dev/null +++ b/OSC/Source/OSC/Private/Common/oscpack/osc/OscTypes.h @@ -0,0 +1,240 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ +#ifndef INCLUDED_OSCPACK_OSCTYPES_H +#define INCLUDED_OSCPACK_OSCTYPES_H + + +namespace osc{ + +// basic types + +#if defined(__BORLANDC__) || defined(_MSC_VER) + +typedef __int64 int64; +typedef unsigned __int64 uint64; + +#elif defined(__x86_64__) || defined(_M_X64) + +typedef long int64; +typedef unsigned long uint64; + +#else + +typedef long long int64; +typedef unsigned long long uint64; + +#endif + + + +#if defined(__x86_64__) || defined(_M_X64) + +typedef signed int int32; +typedef unsigned int uint32; + +#else + +typedef signed long int32; +typedef unsigned long uint32; + +#endif + + +enum ValueTypeSizes{ + OSC_SIZEOF_INT32 = 4, + OSC_SIZEOF_UINT32 = 4, + OSC_SIZEOF_INT64 = 8, + OSC_SIZEOF_UINT64 = 8, +}; + + +// osc_bundle_element_size_t is used for the size of bundle elements and blobs +// the OSC spec specifies these as int32 (signed) but we ensure that they +// are always positive since negative field sizes make no sense. + +typedef int32 osc_bundle_element_size_t; + +enum { + OSC_INT32_MAX = 0x7FFFFFFF, + + // Element sizes are specified to be int32, and are always rounded up to nearest + // multiple of 4. Therefore their values can't be greater than 0x7FFFFFFC. + OSC_BUNDLE_ELEMENT_SIZE_MAX = 0x7FFFFFFC +}; + + +inline bool IsValidElementSizeValue( osc_bundle_element_size_t x ) +{ + // sizes may not be negative or exceed OSC_BUNDLE_ELEMENT_SIZE_MAX + return x >= 0 && x <= OSC_BUNDLE_ELEMENT_SIZE_MAX; +} + + +inline bool IsMultipleOf4( osc_bundle_element_size_t x ) +{ + return (x & ((osc_bundle_element_size_t)0x03)) == 0; +} + + +enum TypeTagValues { + TRUE_TYPE_TAG = 'T', + FALSE_TYPE_TAG = 'F', + NIL_TYPE_TAG = 'N', + INFINITUM_TYPE_TAG = 'I', + INT32_TYPE_TAG = 'i', + FLOAT_TYPE_TAG = 'f', + CHAR_TYPE_TAG = 'c', + RGBA_COLOR_TYPE_TAG = 'r', + MIDI_MESSAGE_TYPE_TAG = 'm', + INT64_TYPE_TAG = 'h', + TIME_TAG_TYPE_TAG = 't', + DOUBLE_TYPE_TAG = 'd', + STRING_TYPE_TAG = 's', + SYMBOL_TYPE_TAG = 'S', + BLOB_TYPE_TAG = 'b', + ARRAY_BEGIN_TYPE_TAG = '[', + ARRAY_END_TYPE_TAG = ']' +}; + + + +// i/o manipulators used for streaming interfaces + +struct BundleInitiator{ + explicit BundleInitiator( uint64 timeTag_ ) : timeTag( timeTag_ ) {} + uint64 timeTag; +}; + +extern BundleInitiator BeginBundleImmediate; + +inline BundleInitiator BeginBundle( uint64 timeTag=1 ) +{ + return BundleInitiator(timeTag); +} + + +struct BundleTerminator{ +}; + +extern BundleTerminator EndBundle; + +struct BeginMessage{ + explicit BeginMessage( const char *addressPattern_ ) : addressPattern( addressPattern_ ) {} + const char *addressPattern; +}; + +struct MessageTerminator{ +}; + +extern MessageTerminator EndMessage; + + +// osc specific types. they are defined as structs so they can be used +// as separately identifiable types with the streaming operators. + +struct NilType{ +}; + +extern NilType OscNil; + +#ifndef _OBJC_OBJC_H_ +extern NilType Nil; // Objective-C defines Nil. so our Nil is deprecated. use OscNil instead +#endif + +struct InfinitumType{ +}; + +extern InfinitumType Infinitum; + +struct RgbaColor{ + RgbaColor() {} + explicit RgbaColor( uint32 value_ ) : value( value_ ) {} + uint32 value; + + operator uint32() const { return value; } +}; + + +struct MidiMessage{ + MidiMessage() {} + explicit MidiMessage( uint32 value_ ) : value( value_ ) {} + uint32 value; + + operator uint32() const { return value; } +}; + + +struct TimeTag{ + TimeTag() {} + explicit TimeTag( uint64 value_ ) : value( value_ ) {} + uint64 value; + + operator uint64() const { return value; } +}; + + +struct Symbol{ + Symbol() {} + explicit Symbol( const char* value_ ) : value( value_ ) {} + const char* value; + + operator const char *() const { return value; } +}; + + +struct Blob{ + Blob() {} + explicit Blob( const void* data_, osc_bundle_element_size_t size_ ) + : data( data_ ), size( size_ ) {} + const void* data; + osc_bundle_element_size_t size; +}; + +struct ArrayInitiator{ +}; + +extern ArrayInitiator BeginArray; + +struct ArrayTerminator{ +}; + +extern ArrayTerminator EndArray; + +} // namespace osc + + +#endif /* INCLUDED_OSCPACK_OSCTYPES_H */ diff --git a/OSC/Source/OSC/Private/Osc.h b/OSC/Source/OSC/Private/Osc.h new file mode 100644 index 0000000..190f651 --- /dev/null +++ b/OSC/Source/OSC/Private/Osc.h @@ -0,0 +1,11 @@ +#pragma once + + +/* Public Dependencies + *****************************************************************************/ + +#include "Networking.h" + + +/* Public Includes + *****************************************************************************/ diff --git a/OSC/Source/OSC/Private/OscModule.cpp b/OSC/Source/OSC/Private/OscModule.cpp new file mode 100644 index 0000000..f66ea05 --- /dev/null +++ b/OSC/Source/OSC/Private/OscModule.cpp @@ -0,0 +1,77 @@ +#include "OscPrivatePCH.h" +#include "OscDispatcher.h" + + +#define LOCTEXT_NAMESPACE "FOscModule" + + +class FOscModule : public IModuleInterface +{ +public: + virtual void StartupModule( ) override + { + if(!FModuleManager::Get().LoadModule(TEXT("Networking")).IsValid()) + { + UE_LOG(LogOSC, Error, TEXT("Required module Networking failed to load")); + return; + } + + auto settings = GetMutableDefault(); + + _dispatcher = UOscDispatcher::Get(); + _dispatcher->Listen(settings->ReceivePort); + settings->UpdateSendAddresses(); + + // register settings + ISettingsModule* settingsModule = ISettingsModule::Get(); + if(settingsModule) + { + FSettingsSectionDelegates settingsDelegates; + settingsDelegates.ModifiedDelegate = FOnSettingsSectionModified::CreateRaw(this, &FOscModule::HandleSettingsSaved); + + settingsModule->RegisterSettings("Project", "Plugins", "OSC", + LOCTEXT("OscSettingsName", "OSC"), + LOCTEXT("OscSettingsDescription", "Configure the OSC plug-in."), + settings, + settingsDelegates + ); + } + else + { + UE_LOG(LogOSC, Warning, TEXT("Settings changed registration failed")); + } + UE_LOG(LogOSC, Display, TEXT("Startup succeed")); + } + + virtual void ShutdownModule( ) override + { + UE_LOG(LogOSC, Display, TEXT("Shutdown")); + if(_dispatcher.IsValid()) + { + _dispatcher->Stop(); + } + } + + bool HandleSettingsSaved() + { + UE_LOG(LogOSC, Display, TEXT("Update settings")); + UOscSettings* settings = GetMutableDefault(); + if(_dispatcher.IsValid()) + { + _dispatcher->Listen(settings->ReceivePort); + } + settings->UpdateSendAddresses(); + return true; + } + +private: + + TWeakObjectPtr _dispatcher; +}; + + +IMPLEMENT_MODULE(FOscModule, OSC); +DEFINE_LOG_CATEGORY(LogOSC); + + +#undef LOCTEXT_NAMESPACE diff --git a/OSC/Source/OSC/Private/OscPrivatePCH.h b/OSC/Source/OSC/Private/OscPrivatePCH.h new file mode 100644 index 0000000..aa96ad9 --- /dev/null +++ b/OSC/Source/OSC/Private/OscPrivatePCH.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Osc.h" + +#include "Core.h" +#include "ModuleManager.h" +#include "Settings.h" +#include "Sockets.h" +#include "SocketSubsystem.h" +#include "TaskGraphInterfaces.h" + +#include "Runtime/Engine/Classes/Kismet/BlueprintFunctionLibrary.h" +#include "Runtime/Networking/Public/Networking.h" + +#include "OscSettings.h" + +#include +#include + +DECLARE_LOG_CATEGORY_EXTERN(LogOSC, Log, All); diff --git a/OSC/Source/OSC/Private/OscSettings.cpp b/OSC/Source/OSC/Private/OscSettings.cpp new file mode 100644 index 0000000..3292d70 --- /dev/null +++ b/OSC/Source/OSC/Private/OscSettings.cpp @@ -0,0 +1,58 @@ +#include "OscPrivatePCH.h" + + +UOscSettings::UOscSettings( const class FPostConstructInitializeProperties& PCIP ) + : Super(PCIP), + ReceivePort(8000), + _sendSocket(FUdpSocketBuilder(TEXT("OscSender")).Build()) +{ + SendTargets.Add(TEXT("127.0.0.1:8000")); +} + +void UOscSettings::UpdateSendAddresses() +{ + FString addressStr, portStr; + for(int32 i=0, n=SendTargets.Num(); i!=n; ++i) + { + if(i >= _sendAddresses.Num()) + { + _sendAddresses.Emplace(FString(), ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr()); + } + + if(_sendAddresses[i].first != SendTargets[i]) + { + int32 sep = 0; + bool isValid = false; + if(SendTargets[i].FindChar(TEXT(':'), sep)) + { + const FString ip = SendTargets[i].Left(sep).Trim(); + _sendAddresses[i].second->SetIp(&ip.GetCharArray()[0], isValid); + if(isValid) + { + const int32 port = FCString::Atoi(&SendTargets[i].GetCharArray()[sep+1]); + _sendAddresses[i].second->SetPort(port); + _sendAddresses[i].first = SendTargets[i]; + } + } + } + } +} + +void UOscSettings::Send(const uint8 *buffer, int32 length, int32 targetIndex) +{ + if(targetIndex < _sendAddresses.Num()) + { + int32 bytesSent = 0; + while(length > 0) + { + _sendSocket->SendTo(buffer, length, bytesSent, *_sendAddresses[targetIndex].second); + length -= bytesSent; + buffer += bytesSent; + } + UE_LOG(LogOSC, Verbose, TEXT("OSC sent")); + } + else + { + UE_LOG(LogOSC, Warning, TEXT("Cannot send OSC: invalid targetIndex %d"), targetIndex); + } +} diff --git a/OSC/Source/OSC/Private/OscSettings.h b/OSC/Source/OSC/Private/OscSettings.h new file mode 100644 index 0000000..b398ce2 --- /dev/null +++ b/OSC/Source/OSC/Private/OscSettings.h @@ -0,0 +1,30 @@ +// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include +#include "OscSettings.generated.h" + + +UCLASS(config=Engine) +class UOscSettings : public UObject +{ + GENERATED_UCLASS_BODY() + +public: + + UPROPERTY(Config, EditAnywhere, Category=Receive) + int32 ReceivePort; + + UPROPERTY(Config, EditAnywhere, Category=Send) + TArray SendTargets; + +public: + void UpdateSendAddresses(); + + void Send(const uint8 *buffer, int32 length, int32 targetIndex); + +private: + TSharedRef _sendSocket; + TArray>> _sendAddresses; +}; diff --git a/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp b/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp new file mode 100644 index 0000000..753c628 --- /dev/null +++ b/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp @@ -0,0 +1,181 @@ + + +#include "OscPrivatePCH.h" +#include "OscDispatcher.h" +#include "OscReceiverInterface.h" + +#include "oscpack/osc/OscReceivedElements.h" + +#include + + +UOscDispatcher::UOscDispatcher(const class FPostConstructInitializeProperties& PCIP) +: Super(PCIP), + _currentPort(0), + _socket(nullptr), + _socketReceiver(nullptr), + _pendingMessages(64) +{ +} + +UOscDispatcher * UOscDispatcher::Get() +{ + return UOscDispatcher::StaticClass()->GetDefaultObject(); +} + +void UOscDispatcher::Listen(uint32_t port) +{ + if(_currentPort != port) + { + delete _socket; + delete _socketReceiver; + + _socket = FUdpSocketBuilder(TEXT("OscListener")).BoundToAddress(FIPv4Address::Any).BoundToPort(port).Build(); + if(_socket) + { + _socketReceiver = new FUdpSocketReceiver(_socket, FTimespan(0, 0, 0, 10), TEXT("OSCListener")); + _socketReceiver->OnDataReceived().BindUObject(this, &UOscDispatcher::Callback); + + _currentPort = port; + UE_LOG(LogOSC, Display, TEXT("Listen to port %d"), port); + } + else + { + UE_LOG(LogOSC, Warning, TEXT("Cannot listen port %d"), port); + } + } +} + +void UOscDispatcher::Stop() +{ + UE_LOG(LogOSC, Display, TEXT("Stop listening")); + if(_socket) + { + _socket->Close(); + } + if(_socketReceiver) + { + _socketReceiver->OnDataReceived().Unbind(); + _socketReceiver->Stop(); + + // wait longer than the socket read wait time, or it will crash. + FPlatformProcess::Sleep(0.1f); + } + + delete _socket; + delete _socketReceiver; + _socket = nullptr; + _socketReceiver = nullptr; + + _currentPort = 0; +} + +void UOscDispatcher::RegisterReceiver(IOscReceiverInterface * receiver) +{ + _receivers.AddUnique(receiver); +} + +void UOscDispatcher::UnregisterReceiver(IOscReceiverInterface * receiver) +{ + _receivers.Remove(receiver); +} + +static void SendMessage(TCircularQueue> & _pendingMessages, const osc::ReceivedMessage & message) +{ + const FName address(message.AddressPattern()); + + FOscDataStruct data; + + const auto argBegin = message.ArgumentsBegin(); + const auto argEnd = message.ArgumentsEnd(); + for(auto it = argBegin; it != argEnd; ++it) + { + FOscDataElemStruct elem; + if(it->IsFloat()) + { + elem.SetFloat(it->AsFloatUnchecked()); + } + else if(it->IsDouble()) + { + elem.SetFloat(it->AsDoubleUnchecked()); + } + else if(it->IsInt32()) + { + elem.SetInt(it->AsInt32Unchecked()); + } + else if(it->IsInt64()) + { + elem.SetInt(it->AsInt64Unchecked()); + } + else if(it->IsBool()) + { + elem.SetBool(it->AsBoolUnchecked()); + } + else if(it->IsString()) + { + elem.SetString(FName(it->AsStringUnchecked())); + } + data.Queue.Add(elem); + } + + // save it in pending messages + _pendingMessages.Enqueue(std::make_pair(address, data)); +} + +static void SendBundle(TCircularQueue> & _pendingMessages, const osc::ReceivedBundle & bundle) +{ + const auto begin = bundle.ElementsBegin(); + const auto end = bundle.ElementsEnd(); + for(auto it = begin; it != end; ++it) + { + if(it->IsBundle()) + { + SendBundle(_pendingMessages, osc::ReceivedBundle(*it)); + } + else + { + SendMessage(_pendingMessages, osc::ReceivedMessage(*it)); + } + } +} + +void UOscDispatcher::Callback(const FArrayReaderPtr& data, const FIPv4Endpoint&) +{ + UE_LOG(LogOSC, Verbose, TEXT("OSC Received")); + const osc::ReceivedPacket packet((const char *)data->GetData(), data->Num()); + if(packet.IsBundle()) + { + SendBundle(_pendingMessages, osc::ReceivedBundle(packet)); + } + else + { + SendMessage(_pendingMessages, osc::ReceivedMessage(packet)); + } + + if(!_pendingMessages.IsEmpty()) + { + FSimpleDelegateGraphTask::CreateAndDispatchWhenReady( + FSimpleDelegateGraphTask::FDelegate::CreateUObject(this, &UOscDispatcher::CallbackMainThread), + TEXT("OscDispatcherProcessMessages"), + nullptr, + ENamedThreads::GameThread); + } +} + +void UOscDispatcher::CallbackMainThread() +{ + std::pair message; + while(_pendingMessages.Dequeue(message)) + { + for(auto receiver : _receivers) + { + receiver->SendEvent(message.first, message.second); + } + } +} + +void UOscDispatcher::BeginDestroy() +{ + Stop(); + Super::BeginDestroy(); +} diff --git a/OSC/Source/OSC/Private/Receive/OscDispatcher.h b/OSC/Source/OSC/Private/Receive/OscDispatcher.h new file mode 100644 index 0000000..a4f0a28 --- /dev/null +++ b/OSC/Source/OSC/Private/Receive/OscDispatcher.h @@ -0,0 +1,48 @@ +#pragma once + +#include "OscDataStruct.h" +#include "OscDispatcher.generated.h" + +class IOscReceiverInterface; +class FSocket; +class FUdpSocketReceiver; + + +/// Singleton that dispatches the OSC messages to listeners. +UCLASS() +class UOscDispatcher : public UObject +{ + GENERATED_UCLASS_BODY() + + friend class OscModule; + + void Listen(uint32_t port); + void Stop(); + +public: + + /// Get the unique instance + /// + /// The instance is the UObject default instance. + static UOscDispatcher * Get(); + + /// Add the receiver in the listeners list + void RegisterReceiver(IOscReceiverInterface * receiver); + + /// Remove the receiver in the listeners list + void UnregisterReceiver(IOscReceiverInterface * receiver); + +private: + void Callback(const FArrayReaderPtr& data, const FIPv4Endpoint&); + + void CallbackMainThread(); + + void BeginDestroy() override; + +private: + TArray _receivers; + uint32_t _currentPort; + FSocket * _socket; + FUdpSocketReceiver * _socketReceiver; + TCircularQueue> _pendingMessages; +}; diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverComponent.cpp b/OSC/Source/OSC/Private/Receive/OscReceiverComponent.cpp new file mode 100644 index 0000000..50eba74 --- /dev/null +++ b/OSC/Source/OSC/Private/Receive/OscReceiverComponent.cpp @@ -0,0 +1,32 @@ +#include "OscPrivatePCH.h" +#include "OscReceiverComponent.h" +#include "OscDispatcher.h" + + +UOscReceiverComponent::UOscReceiverComponent(const class FPostConstructInitializeProperties& PCIP) + : Super(PCIP) +{ + +} + +void UOscReceiverComponent::OnRegister() +{ + Super::OnRegister(); + + auto instance = UOscDispatcher::Get(); + if (instance && !HasAnyFlags(RF_ClassDefaultObject)) + { + instance->RegisterReceiver(this); + } +} + +void UOscReceiverComponent::OnUnregister() +{ + auto instance = UOscDispatcher::Get(); + if (instance && !HasAnyFlags(RF_ClassDefaultObject)) + { + instance->UnregisterReceiver(this); + } + + Super::OnUnregister(); +} diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverComponent.h b/OSC/Source/OSC/Private/Receive/OscReceiverComponent.h new file mode 100644 index 0000000..365708f --- /dev/null +++ b/OSC/Source/OSC/Private/Receive/OscReceiverComponent.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Components/ActorComponent.h" +#include "OscReceiverInterface.h" +#include "OscDataStruct.h" +#include "OscReceiverComponent.generated.h" + + +// declare the OnOscReceived event type +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComponentOscReceivedSignature, const FName &, Address, const FOscDataStruct &, Data); + + +UCLASS(meta = (BlueprintSpawnableComponent)) +class UOscReceiverComponent : public UActorComponent, public IOscReceiverInterface +{ + GENERATED_UCLASS_BODY() + + UPROPERTY(EditAnywhere, Category=OSC) + FString AddressFilter; + + UPROPERTY(BlueprintAssignable, Category=OSC) + FComponentOscReceivedSignature OnOscReceived; + +protected: + virtual void OnRegister() override; + + virtual void OnUnregister() override; + + virtual const FString & GetAddressFilter() override + { + return AddressFilter; + } + + virtual void SendEvent(const FName & Address, const FOscDataStruct & Data) override + { + OnOscReceived.Broadcast(Address, Data); + } +}; diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverInterface.cpp b/OSC/Source/OSC/Private/Receive/OscReceiverInterface.cpp new file mode 100644 index 0000000..870124d --- /dev/null +++ b/OSC/Source/OSC/Private/Receive/OscReceiverInterface.cpp @@ -0,0 +1,18 @@ +#include "OscPrivatePCH.h" +#include "OscReceiverInterface.h" + + +UOscReceiverInterface::UOscReceiverInterface(const class FPostConstructInitializeProperties& PCIP) +: Super(PCIP) +{ + +} + +const FString & IOscReceiverInterface::GetAddressFilter() +{ + static FString defaultValue; + return defaultValue; +} + +void IOscReceiverInterface::SendEvent(const FName & Address, const FOscDataStruct & Data) +{ } diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverInterface.h b/OSC/Source/OSC/Private/Receive/OscReceiverInterface.h new file mode 100644 index 0000000..d776015 --- /dev/null +++ b/OSC/Source/OSC/Private/Receive/OscReceiverInterface.h @@ -0,0 +1,19 @@ +#pragma once + +#include "OscDataStruct.h" +#include "OscReceiverInterface.generated.h" + + +UINTERFACE(MinimalAPI) +class UOscReceiverInterface : public UInterface +{ + GENERATED_UINTERFACE_BODY() +}; + +class IOscReceiverInterface +{ + GENERATED_IINTERFACE_BODY() + + virtual const FString & GetAddressFilter(); + virtual void SendEvent(const FName & Address, const FOscDataStruct & Data); +}; diff --git a/README.md b/README.md index eb0bf26..af01c8e 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,25 @@ UE4-OSC ======= OSC plugin for Unreal Engine 4 Blueprints + + +Integration +=========== + +Put the "OSC" folder in a "Plugins" folder in the project root. +e.g. "/MyProject/Plugins/OSC" + + +Usage +===== + + - Receiver: +Set the listening port in the plugin settings. +Add a OscReceiverComponent to your Blueprint class. +Bind the OnOscReceived custom event. +Use the PopFloat/Int/Bool/String functions to retrieve the OSC message parameters. + + - Sender: +Set the "send to" addresses in the plugin settings. +Use the PushFloat/Int/Bool/String functions to build the OSC message parameters. +Use the SendOsc function to send the OSC message