mirror of
https://github.com/storytold/UE4-OSC.git
synced 2026-10-09 00:09:43 +00:00
add the example Project "OscDemo"
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
[EditoronlyBP]
|
||||
bAllowClassAndBlueprintPinMatching=true
|
||||
bReplaceBlueprintWithClass=true
|
||||
bDontLoadBlueprintOutsideEditor=true
|
||||
bBlueprintIsNotBlueprintType=true
|
||||
@@ -0,0 +1,12 @@
|
||||
[URL]
|
||||
|
||||
[/Script/HardwareTargeting.HardwareTargetingSettings]
|
||||
TargetedHardwareClass=Desktop
|
||||
AppliedTargetedHardwareClass=Desktop
|
||||
DefaultGraphicsPerformance=Maximum
|
||||
AppliedDefaultGraphicsPerformance=Maximum
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
EditorStartupMap=/Game/Map.Map
|
||||
GameDefaultMap=/Game/Map.Map
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[/Script/EngineSettings.GeneralProjectSettings]
|
||||
ProjectID=316644CF469FFDAC26DC92ADB05D7359
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "4.11",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "OscDemo",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,53 @@
|
||||
namespace UnrealBuildTool.Rules
|
||||
{
|
||||
public class OSC : ModuleRules
|
||||
{
|
||||
public OSC(TargetInfo Target)
|
||||
{
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Networking",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Sockets",
|
||||
"InputCore",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[] {
|
||||
"Settings",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"OSC/Private",
|
||||
"OSC/Private/Common",
|
||||
"OSC/Private/Receive",
|
||||
"OSC/Private/Send",
|
||||
}
|
||||
);
|
||||
|
||||
if (Target.Type.HasValue && TargetRules.IsEditorType(Target.Type.Value))
|
||||
{
|
||||
Definitions.Add("OSC_EDITOR_BUILD=1");
|
||||
|
||||
PrivateDependencyModuleNames.Add("UnrealEd");
|
||||
}
|
||||
else
|
||||
{
|
||||
Definitions.Add("OSC_EDITOR_BUILD=0");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataElemStruct.generated.h"
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
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<int64 *>(&value);
|
||||
check(value == AsFloatValue());
|
||||
}
|
||||
|
||||
double AsFloatValue() const
|
||||
{
|
||||
check(IsFloat());
|
||||
return *reinterpret_cast<const double *>(&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;
|
||||
check(value == AsIntValue());
|
||||
}
|
||||
|
||||
int64 AsIntValue() const
|
||||
{
|
||||
check(IsInt());
|
||||
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;
|
||||
check(value == AsBoolValue());
|
||||
}
|
||||
|
||||
bool AsBoolValue() const
|
||||
{
|
||||
check(IsBool());
|
||||
return Data != 0;
|
||||
}
|
||||
|
||||
bool IsBool() const
|
||||
{
|
||||
return Type == BOOL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void SetString(FName value)
|
||||
{
|
||||
static_assert(sizeof(FMinimalName) <= sizeof(Data), "Too short to hold the string value.");
|
||||
const auto mininal = NameToMinimalName(value);
|
||||
Data = *reinterpret_cast<const int64*>(&mininal);
|
||||
Type = STRING;
|
||||
check(value == AsStringValue());
|
||||
}
|
||||
|
||||
FName AsStringValue() const
|
||||
{
|
||||
check(IsString());
|
||||
return MinimalNameToName(*reinterpret_cast<const FMinimalName *>(&Data));
|
||||
}
|
||||
|
||||
bool IsString() const
|
||||
{
|
||||
return Type == STRING;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/// Cast the value to T.
|
||||
template <class T>
|
||||
T GetValue() const
|
||||
{
|
||||
static const bool isNumber = (std::is_integral<T>::value || std::is_floating_point<T>::value);
|
||||
if(IsFloat())
|
||||
{
|
||||
if(isNumber)
|
||||
{
|
||||
const auto value = AsFloatValue();
|
||||
return Cast<T, isNumber>::eval(value);
|
||||
}
|
||||
}
|
||||
else if(IsInt())
|
||||
{
|
||||
if(isNumber)
|
||||
{
|
||||
const auto value = AsIntValue();
|
||||
return Cast<T, isNumber>::eval(value);
|
||||
}
|
||||
}
|
||||
else if(IsBool())
|
||||
{
|
||||
if(isNumber)
|
||||
{
|
||||
const auto value = AsBoolValue();
|
||||
return Cast<T, isNumber>::eval(value);
|
||||
}
|
||||
}
|
||||
else if(IsString())
|
||||
{
|
||||
const bool isString = std::is_same<T, FName>::value;
|
||||
if(isString)
|
||||
{
|
||||
const auto value = AsStringValue();
|
||||
return Cast<T, isString>::eval(value);
|
||||
}
|
||||
}
|
||||
return T();
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T, bool>
|
||||
struct Cast
|
||||
{
|
||||
template <class U>
|
||||
static T eval(const U &)
|
||||
{
|
||||
return T();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
struct FOscDataElemStruct::Cast<T, true>
|
||||
{
|
||||
static T eval(double value)
|
||||
{
|
||||
if(std::is_integral<T>::value)
|
||||
{
|
||||
value += 0.4999999;
|
||||
}
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
static T eval(const U & value)
|
||||
{
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct FOscDataElemStruct::Cast<bool, true>
|
||||
{
|
||||
template <class U>
|
||||
static bool eval(const U & value)
|
||||
{
|
||||
return value != 0;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataElemStruct.h"
|
||||
#include "OscDataStruct.generated.h"
|
||||
|
||||
|
||||
USTRUCT()
|
||||
struct FOscDataStruct
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
int32 Index;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<FOscDataElemStruct> Queue;
|
||||
|
||||
FOscDataStruct() : Index(0)
|
||||
{ }
|
||||
|
||||
FOscDataElemStruct Pop()
|
||||
{
|
||||
const auto idx = std::max(Index, 0);
|
||||
if(idx < Queue.Num())
|
||||
{
|
||||
++Index;
|
||||
return Queue[idx];
|
||||
}
|
||||
return FOscDataElemStruct();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,285 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscSettings.h"
|
||||
#include "OscFunctionLibrary.h"
|
||||
#include "oscpack/osc/OscOutboundPacketStream.h"
|
||||
|
||||
|
||||
template <class T>
|
||||
static inline
|
||||
void PopValueImpl(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, T & Value)
|
||||
{
|
||||
if(input.Num() > 0)
|
||||
{
|
||||
output.Reserve(input.Num() - 1);
|
||||
for(int32 i=1, n=input.Num(); i!=n; ++i)
|
||||
{
|
||||
output.Add(input[i]);
|
||||
}
|
||||
Value = input[0].GetValue<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Empty();
|
||||
Value = FOscDataElemStruct().GetValue<T>();
|
||||
}
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PopBool(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, bool & Value)
|
||||
{
|
||||
PopValueImpl(input, output, Value);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PopFloat(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, float & Value)
|
||||
{
|
||||
PopValueImpl(input, output, Value);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PopInt(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, int32 & Value)
|
||||
{
|
||||
PopValueImpl(input, output, Value);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PopString(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, FName & Value)
|
||||
{
|
||||
PopValueImpl(input, output, Value);
|
||||
}
|
||||
|
||||
|
||||
void UOscFunctionLibrary::PushBool(const TArray<FOscDataElemStruct> & input, bool Value, TArray<FOscDataElemStruct> & output)
|
||||
{
|
||||
output = input;
|
||||
FOscDataElemStruct elem;
|
||||
elem.SetBool(Value);
|
||||
output.Add(elem);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PushFloat(const TArray<FOscDataElemStruct> & input, float Value, TArray<FOscDataElemStruct> & output)
|
||||
{
|
||||
output = input;
|
||||
FOscDataElemStruct elem;
|
||||
elem.SetFloat(Value);
|
||||
output.Add(elem);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PushInt(const TArray<FOscDataElemStruct> & input, int32 Value, TArray<FOscDataElemStruct> & output)
|
||||
{
|
||||
output = input;
|
||||
FOscDataElemStruct elem;
|
||||
elem.SetInt(Value);
|
||||
output.Add(elem);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PushString(const TArray<FOscDataElemStruct> & input, FName Value, TArray<FOscDataElemStruct> & output)
|
||||
{
|
||||
if(Value.GetDisplayNameEntry()->IsWide())
|
||||
{
|
||||
const auto tmp = Value.GetPlainNameString();
|
||||
UE_LOG(LogOSC, Error, TEXT("Invalid string argument \"%s\": ASCII only"), *tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
output = input;
|
||||
FOscDataElemStruct elem;
|
||||
elem.SetString(Value);
|
||||
output.Add(elem);
|
||||
}
|
||||
|
||||
|
||||
bool UOscFunctionLibrary::AsBool(const FOscDataElemStruct & input)
|
||||
{
|
||||
return input.GetValue<bool>();
|
||||
}
|
||||
|
||||
float UOscFunctionLibrary::AsFloat(const FOscDataElemStruct & input)
|
||||
{
|
||||
return input.GetValue<float>();
|
||||
}
|
||||
|
||||
int32 UOscFunctionLibrary::AsInt(const FOscDataElemStruct & input)
|
||||
{
|
||||
return input.GetValue<int32>();
|
||||
}
|
||||
|
||||
FName UOscFunctionLibrary::AsString(const FOscDataElemStruct & input)
|
||||
{
|
||||
return input.GetValue<FName>();
|
||||
}
|
||||
|
||||
|
||||
FOscDataElemStruct UOscFunctionLibrary::FromBool(bool input)
|
||||
{
|
||||
FOscDataElemStruct result;
|
||||
result.SetBool(input);
|
||||
return result;
|
||||
}
|
||||
|
||||
FOscDataElemStruct UOscFunctionLibrary::FromFloat(float input)
|
||||
{
|
||||
FOscDataElemStruct result;
|
||||
result.SetFloat(input);
|
||||
return result;
|
||||
}
|
||||
|
||||
FOscDataElemStruct UOscFunctionLibrary::FromInt(int32 input)
|
||||
{
|
||||
FOscDataElemStruct result;
|
||||
result.SetInt(input);
|
||||
return result;
|
||||
}
|
||||
|
||||
FOscDataElemStruct UOscFunctionLibrary::FromString(FName input)
|
||||
{
|
||||
FOscDataElemStruct result;
|
||||
result.SetString(input);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isValidAddress(const FName & Address)
|
||||
{
|
||||
if(!Address.IsValid())
|
||||
{
|
||||
UE_LOG(LogOSC, Error, TEXT("Empty OSC address"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Address.GetDisplayNameEntry()->IsWide())
|
||||
{
|
||||
const auto tmp = Address.GetPlainNameString();
|
||||
UE_LOG(LogOSC, Error, TEXT("Invalid OSC address \"%s\": ASCII only"), *tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Address.GetPlainANSIString()[0] != '/')
|
||||
{
|
||||
const auto tmp = Address.GetPlainNameString();
|
||||
UE_LOG(LogOSC, Error, TEXT("Invalid OSC address \"%s\": must start with '/'"), *tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void appendMessage(osc::OutboundPacketStream & output, FName Address, const TArray<FOscDataElemStruct> & Data)
|
||||
{
|
||||
output << osc::BeginMessage(Address.GetPlainANSIString());
|
||||
if(output.State() != osc::SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(const auto & elem : Data)
|
||||
{
|
||||
if(elem.IsFloat())
|
||||
{
|
||||
output << (float)elem.AsFloatValue();
|
||||
}
|
||||
else if(elem.IsInt())
|
||||
{
|
||||
output << (int32)elem.AsIntValue();
|
||||
}
|
||||
else if(elem.IsBool())
|
||||
{
|
||||
output << elem.AsBoolValue();
|
||||
}
|
||||
else if(elem.IsString())
|
||||
{
|
||||
output << elem.AsStringValue().GetPlainANSIString();
|
||||
}
|
||||
|
||||
if(output.State() != osc::SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
output << osc::EndMessage;
|
||||
}
|
||||
|
||||
TArray<uint8> GlobalBuffer(TArray<uint8>(), 1024);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::SendOsc(FName Address, const TArray<FOscDataElemStruct> & Data, int32 TargetIndex)
|
||||
{
|
||||
if(!isValidAddress(Address))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static_assert(sizeof(uint8) == sizeof(char), "Cannot cast uint8 to char");
|
||||
|
||||
osc::OutboundPacketStream output((char *)GlobalBuffer.GetData(), GlobalBuffer.Max());
|
||||
check(reinterpret_cast<const void *>(GlobalBuffer.GetData()) == reinterpret_cast<const void *>(output.Data()));
|
||||
|
||||
appendMessage(output, Address, Data);
|
||||
|
||||
if(output.State() == osc::OUT_OF_BUFFER_MEMORY_ERROR)
|
||||
{
|
||||
GlobalBuffer.Reserve(GlobalBuffer.Max() * 2); // not enough memory: double the size
|
||||
SendOsc(Address, Data, TargetIndex); // try again
|
||||
return;
|
||||
}
|
||||
|
||||
if(output.State() == osc::SUCCESS)
|
||||
{
|
||||
GetMutableDefault<UOscSettings>()->Send(GlobalBuffer.GetData(), output.Size(), TargetIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Error, TEXT("OSC Send Message Error: %s"), osc::errorString(output.State()));
|
||||
}
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::SendOscBundle(const TArray<FOscMessageStruct> & Messages, int32 TargetIndex)
|
||||
{
|
||||
static_assert(sizeof(uint8) == sizeof(char), "Cannot cast uint8 to char");
|
||||
|
||||
osc::OutboundPacketStream output((char *)GlobalBuffer.GetData(), GlobalBuffer.Max());
|
||||
check(reinterpret_cast<const void *>(GlobalBuffer.GetData()) == reinterpret_cast<const void *>(output.Data()));
|
||||
|
||||
output << osc::BeginBundle();
|
||||
for(const auto & message : Messages)
|
||||
{
|
||||
if(!isValidAddress(message.Address))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
appendMessage(output, message.Address, message.Data);
|
||||
|
||||
if(output.State() == osc::OUT_OF_BUFFER_MEMORY_ERROR)
|
||||
{
|
||||
GlobalBuffer.Reserve(GlobalBuffer.Max() * 2); // not enough memory: double the size
|
||||
SendOscBundle(Messages, TargetIndex); // try again
|
||||
return;
|
||||
}
|
||||
if(output.State() != osc::SUCCESS)
|
||||
{
|
||||
UE_LOG(LogOSC, Error, TEXT("OSC Send Bundle Error: %s"), osc::errorString(output.State()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
output << osc::EndBundle;
|
||||
|
||||
if(output.State() == osc::OUT_OF_BUFFER_MEMORY_ERROR)
|
||||
{
|
||||
GlobalBuffer.Reserve(GlobalBuffer.Max() * 2); // not enough memory: double the size
|
||||
SendOscBundle(Messages, TargetIndex); // try again
|
||||
return;
|
||||
}
|
||||
|
||||
if(output.State() == osc::SUCCESS)
|
||||
{
|
||||
GetMutableDefault<UOscSettings>()->Send(GlobalBuffer.GetData(), output.Size(), TargetIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Error, TEXT("OSC Send Bundle Error: %s"), osc::errorString(output.State()));
|
||||
}
|
||||
}
|
||||
|
||||
int32 UOscFunctionLibrary::AddSendOscTarget(FString IpPort)
|
||||
{
|
||||
return GetMutableDefault<UOscSettings>()->GetOrAddSendTarget(IpPort);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataElemStruct.h"
|
||||
#include "OscMessageStruct.h"
|
||||
#include "OscFunctionLibrary.generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UOscFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/// Get the next value from an OSC message as a boolean.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PopBool(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, bool & Value);
|
||||
|
||||
/// Get the next value from an OSC message as a floating point.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PopFloat(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, float & Value);
|
||||
|
||||
/// Get the next value from an OSC message as a integer.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PopInt(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, int32 & Value);
|
||||
|
||||
/// Get the next value from an OSC message as a string.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PopString(const TArray<FOscDataElemStruct> & input, TArray<FOscDataElemStruct> & output, FName & Value);
|
||||
|
||||
|
||||
/// Add a boolean value to an OSC message.
|
||||
UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input"))
|
||||
static void PushBool(const TArray<FOscDataElemStruct> & input, bool Value, TArray<FOscDataElemStruct> & output);
|
||||
|
||||
/// Add a floating point value to an OSC message.
|
||||
UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input"))
|
||||
static void PushFloat(const TArray<FOscDataElemStruct> & input, float Value, TArray<FOscDataElemStruct> & output);
|
||||
|
||||
/// Add a integer value to an OSC message.
|
||||
UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input"))
|
||||
static void PushInt(const TArray<FOscDataElemStruct> & input, int32 Value, TArray<FOscDataElemStruct> & output);
|
||||
|
||||
/// Add a string value to an OSC message.
|
||||
UFUNCTION(BlueprintPure, Category=OSC, meta=(AutoCreateRefTerm = "input"))
|
||||
static void PushString(const TArray<FOscDataElemStruct> & input, FName Value, TArray<FOscDataElemStruct> & output);
|
||||
|
||||
|
||||
/// Interpret an OSC argument as a boolean
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static bool AsBool(const FOscDataElemStruct & input);
|
||||
|
||||
/// Interpret an OSC argument as a floating point.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static float AsFloat(const FOscDataElemStruct & input);
|
||||
|
||||
/// Interpret an OSC argument as a integer.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static int32 AsInt(const FOscDataElemStruct & input);
|
||||
|
||||
/// Interpret an OSC argument as a string.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static FName AsString(const FOscDataElemStruct & input);
|
||||
|
||||
|
||||
/// Create an OSC argument from a boolean
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static FOscDataElemStruct FromBool(bool input);
|
||||
|
||||
/// Create an OSC argument from a floating point.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static FOscDataElemStruct FromFloat(float input);
|
||||
|
||||
/// Create an OSC argument from a integer.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static FOscDataElemStruct FromInt(int32 input);
|
||||
|
||||
/// Create an OSC argument from a string.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static FOscDataElemStruct FromString(FName input);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send an OSC message.
|
||||
* @param Address OSC address.
|
||||
* @param Data result of successive PushFloat/Int/String/etc.
|
||||
* @param TargetIndex index of the destination, -1 for all destinations. (SendTarget list of the plugin settings)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category=OSC, meta=(AutoCreateRefTerm = "Data"))
|
||||
static void SendOsc(FName Address, const TArray<FOscDataElemStruct> & Data, int32 TargetIndex);
|
||||
|
||||
/**
|
||||
* @brief Send several OSC messages in an OSC bundle.
|
||||
* @param Messages of the bundle.
|
||||
* @param TargetIndex index of the destination, -1 for all destinations. (SendTarget list of the plugin settings)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category=OSC)
|
||||
static void SendOscBundle(const TArray<FOscMessageStruct> & Messages, int32 TargetIndex);
|
||||
|
||||
/**
|
||||
* @brief Add Ip:Port to the available OSC send targets.
|
||||
* @param IpPort "ip:port". e.g. "192.168.0.1:7777"
|
||||
* @return The created TargetIndex to pass to the SendOsc function.
|
||||
*
|
||||
* Use this function to add target at runtime. Generally, it is best
|
||||
* to define your targets in the OSC plugin settings.
|
||||
*
|
||||
* @see SendOsc
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category=OSC)
|
||||
static int32 AddSendOscTarget(FString IpPort);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataElemStruct.h"
|
||||
#include "OscMessageStruct.generated.h"
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FOscMessageStruct
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FName Address;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<FOscDataElemStruct> Data;
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
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 <exception>
|
||||
|
||||
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 */
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
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 */
|
||||
|
||||
+735
@@ -0,0 +1,735 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
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 <malloc.h> // for alloca
|
||||
#else
|
||||
//#include <alloca.h> // alloca on Linux (also OSX)
|
||||
#include <stdlib.h> // alloca on OSX and FreeBSD (and Linux?)
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring> // memcpy, memmove, strcpy, strlen
|
||||
#include <cstddef> // 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<int32*>(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<uint32*>(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<int64*>(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<uint64*>(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 )
|
||||
, state_( SUCCESS )
|
||||
{
|
||||
// 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<uint32*>(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<uint32*>(beginPtr) =
|
||||
(uint32)(reinterpret_cast<char*>(elementSizePtr_) - data_);
|
||||
|
||||
elementSizePtr_ = reinterpret_cast<uint32*>(beginPtr);
|
||||
|
||||
return beginPtr + 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::EndElement( char *endPtr )
|
||||
{
|
||||
assert( elementSizePtr_ != 0 );
|
||||
|
||||
if( elementSizePtr_ == reinterpret_cast<uint32*>(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<uint32*>(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<char*>(elementSizePtr_);
|
||||
// assert( d >= 4 && d <= 0x7FFFFFFF ); // assume packets smaller than 2Gb
|
||||
|
||||
uint32 elementSize = static_cast<uint32>(d - 4);
|
||||
FromUInt32( reinterpret_cast<char*>(elementSizePtr_), elementSize );
|
||||
|
||||
// finally, we reset the element size ptr to the containing element
|
||||
elementSizePtr_ = previousElementSizePtr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool OutboundPacketStream::ElementSizeSlotRequired() const
|
||||
{
|
||||
return (elementSizePtr_ != 0);
|
||||
}
|
||||
|
||||
|
||||
Errors OutboundPacketStream::CheckForAvailableBundleSpace()
|
||||
{
|
||||
std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) + 16;
|
||||
return (required > Capacity()) ? OUT_OF_BUFFER_MEMORY_ERROR : SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
Errors 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;
|
||||
|
||||
return (required > Capacity()) ? OUT_OF_BUFFER_MEMORY_ERROR : SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
Errors 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 );
|
||||
|
||||
return (required > Capacity()) ? OUT_OF_BUFFER_MEMORY_ERROR : SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::Clear()
|
||||
{
|
||||
typeTagsCurrent_ = end_;
|
||||
messageCursor_ = data_;
|
||||
argumentCurrent_ = data_;
|
||||
elementSizePtr_ = 0;
|
||||
messageIsInProgress_ = false;
|
||||
state_ = SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
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 )
|
||||
{
|
||||
check( !IsMessageInProgress() )
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableBundleSpace();
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
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;
|
||||
|
||||
check( IsBundleInProgress() )
|
||||
check( !IsMessageInProgress() )
|
||||
check( state_ == SUCCESS )
|
||||
|
||||
EndElement( messageCursor_ );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs )
|
||||
{
|
||||
check( !IsMessageInProgress() )
|
||||
check( state_ == SUCCESS )
|
||||
|
||||
state_ = CheckForAvailableMessageSpace( rhs.addressPattern );
|
||||
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
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;
|
||||
|
||||
check( IsMessageInProgress() )
|
||||
check( state_ == SUCCESS )
|
||||
|
||||
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 )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(0);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(0);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = NIL_TYPE_TAG;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(0);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = INFINITUM_TYPE_TAG;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(4);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = INT32_TYPE_TAG;
|
||||
FromInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( float rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(4);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--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<float*>(argumentCurrent_) = rhs;
|
||||
#endif
|
||||
|
||||
argumentCurrent_ += 4;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( char rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(4);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = CHAR_TYPE_TAG;
|
||||
FromInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(4);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG;
|
||||
FromUInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(4);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG;
|
||||
FromUInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(8);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = INT64_TYPE_TAG;
|
||||
FromInt64( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 8;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(8);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG;
|
||||
FromUInt64( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 8;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( double rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(8);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--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<double*>(argumentCurrent_) = rhs;
|
||||
#endif
|
||||
|
||||
argumentCurrent_ += 8;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) );
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--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 )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) );
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--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 )
|
||||
{
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) );
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--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;
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(0);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = ARRAY_BEGIN_TYPE_TAG;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayTerminator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
check( state_ == SUCCESS )
|
||||
state_ = CheckForAvailableArgumentSpace(0);
|
||||
if(state_ == SUCCESS)
|
||||
{
|
||||
*(--typeTagsCurrent_) = ARRAY_END_TYPE_TAG;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
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 <cstring> // size_t
|
||||
|
||||
#include "OscTypes.h"
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
|
||||
class OutboundPacketStream{
|
||||
public:
|
||||
OutboundPacketStream( char *buffer, std::size_t capacity );
|
||||
~OutboundPacketStream();
|
||||
|
||||
Errors State() const { return state_; }
|
||||
|
||||
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;
|
||||
Errors CheckForAvailableBundleSpace();
|
||||
Errors CheckForAvailableMessageSpace( const char *addressPattern );
|
||||
Errors 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_;
|
||||
|
||||
Errors state_;
|
||||
};
|
||||
|
||||
} // namespace osc
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H */
|
||||
+903
@@ -0,0 +1,903 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
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 <cstddef> // 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(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == TRUE_TYPE_TAG )
|
||||
return true;
|
||||
else if( *typeTagPtr_ == FALSE_TYPE_TAG )
|
||||
return false;
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ReceivedMessageArgument::AsBoolUnchecked(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == TRUE_TYPE_TAG )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int32 ReceivedMessageArgument::AsInt32(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == INT32_TYPE_TAG )
|
||||
return AsInt32Unchecked();
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == FLOAT_TYPE_TAG )
|
||||
return AsFloatUnchecked();
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
|
||||
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(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == CHAR_TYPE_TAG )
|
||||
return AsCharUnchecked();
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return '\0';
|
||||
}
|
||||
|
||||
|
||||
char ReceivedMessageArgument::AsCharUnchecked() const
|
||||
{
|
||||
return (char)ToInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsRgbaColor(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == RGBA_COLOR_TYPE_TAG )
|
||||
return AsRgbaColorUnchecked();
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const
|
||||
{
|
||||
return ToUInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsMidiMessage(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG )
|
||||
return AsMidiMessageUnchecked();
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const
|
||||
{
|
||||
return ToUInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
int64 ReceivedMessageArgument::AsInt64(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == INT64_TYPE_TAG )
|
||||
return AsInt64Unchecked();
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return 0LL;
|
||||
}
|
||||
|
||||
|
||||
int64 ReceivedMessageArgument::AsInt64Unchecked() const
|
||||
{
|
||||
return ToInt64( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedMessageArgument::AsTimeTag(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == TIME_TAG_TYPE_TAG )
|
||||
return AsTimeTagUnchecked();
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const
|
||||
{
|
||||
return ToUInt64( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
double ReceivedMessageArgument::AsDouble(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == DOUBLE_TYPE_TAG )
|
||||
return AsDoubleUnchecked();
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return 0.;
|
||||
}
|
||||
|
||||
|
||||
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(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == STRING_TYPE_TAG )
|
||||
return argumentPtr_;
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
const char* ReceivedMessageArgument::AsSymbol(Errors & state) const
|
||||
{
|
||||
state = SUCCESS;
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == SYMBOL_TYPE_TAG )
|
||||
return argumentPtr_;
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
void ReceivedMessageArgument::AsBlob( const void*& data, osc_bundle_element_size_t& size, Errors & state ) const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
state = MISSING_ARGUMENT_ERROR;
|
||||
else if( *typeTagPtr_ == BLOB_TYPE_TAG )
|
||||
AsBlobUnchecked( data, size, state );
|
||||
else
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size, Errors & state ) 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) )
|
||||
{
|
||||
state = MALFORMED_MESSAGE_INVALID_BLOB_SIZE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = SUCCESS;
|
||||
size = sizeResult;
|
||||
data = (void*)(argumentPtr_+ osc::OSC_SIZEOF_INT32);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t ReceivedMessageArgument::ComputeArrayItemCount(Errors & state) const
|
||||
{
|
||||
// it is only valid to call ComputeArrayItemCount when the argument is the array start marker
|
||||
if( !IsArrayBegin() )
|
||||
{
|
||||
state = WRONG_ARGUMENT_TYPE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
state = SUCCESS;
|
||||
|
||||
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() )
|
||||
, state_( SUCCESS )
|
||||
{
|
||||
Init( packet.Contents(), packet.Size() );
|
||||
}
|
||||
|
||||
|
||||
ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement )
|
||||
: addressPattern_( bundleElement.Contents() )
|
||||
, state_( SUCCESS )
|
||||
{
|
||||
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) )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_INVALID_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if( size == 0 )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_ZERO_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_NOT_MULTIPLE_OF_4_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
const char *end = message + size;
|
||||
|
||||
typeTagsBegin_ = FindStr4End( addressPattern_, end );
|
||||
if( typeTagsBegin_ == 0 ){
|
||||
// address pattern was not terminated before end
|
||||
state_ = MALFORMED_MESSAGE_UNTERMINATED_ADDRESS_PATTERN_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if( typeTagsBegin_ == end ){
|
||||
// message consists of only the address pattern - no arguments or type tags.
|
||||
typeTagsBegin_ = 0;
|
||||
typeTagsEnd_ = 0;
|
||||
arguments_ = 0;
|
||||
|
||||
}else{
|
||||
if( *typeTagsBegin_ != ',' )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_NO_TYPE_TAGS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_UNTERMINATED_TYPE_TAGS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
++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 )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
argument += 4;
|
||||
if( argument > end )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case INT64_TYPE_TAG:
|
||||
case TIME_TAG_TYPE_TAG:
|
||||
case DOUBLE_TYPE_TAG:
|
||||
|
||||
if( argument == end )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
argument += 8;
|
||||
if( argument > end )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case STRING_TYPE_TAG:
|
||||
case SYMBOL_TYPE_TAG:
|
||||
|
||||
if( argument == end )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
argument = FindStr4End( argument, end );
|
||||
if( argument == 0 )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_UNTERMINATED_STRING_ERROR;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOB_TYPE_TAG:
|
||||
{
|
||||
if( argument + osc::OSC_SIZEOF_INT32 > end )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
state_ = MALFORMED_MESSAGE_UNKNOWN_TYPE_TAG_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
}while( *++typeTag != '\0' );
|
||||
typeTagsEnd_ = typeTag;
|
||||
|
||||
if( arrayLevel != 0 )
|
||||
{
|
||||
state_ = MALFORMED_MESSAGE_UNTERMINATED_ARRAY_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 )
|
||||
, state_( SUCCESS )
|
||||
{
|
||||
Init( packet.Contents(), packet.Size() );
|
||||
}
|
||||
|
||||
|
||||
ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement )
|
||||
: elementCount_( 0 )
|
||||
, state_( SUCCESS )
|
||||
{
|
||||
Init( bundleElement.Contents(), bundleElement.Size() );
|
||||
}
|
||||
|
||||
|
||||
void ReceivedBundle::Init( const char *bundle, osc_bundle_element_size_t size )
|
||||
{
|
||||
|
||||
if( !IsValidElementSizeValue(size) )
|
||||
{
|
||||
state_ = MALFORMED_BUNDLE_INVALID_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if( size < 16 )
|
||||
{
|
||||
state_ = MALFORMED_BUNDLE_TOO_SHORT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
{
|
||||
state_ = MALFORMED_BUNDLE_NOT_MULTIPLE_OF_4_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if( bundle[0] != '#'
|
||||
|| bundle[1] != 'b'
|
||||
|| bundle[2] != 'u'
|
||||
|| bundle[3] != 'n'
|
||||
|| bundle[4] != 'd'
|
||||
|| bundle[5] != 'l'
|
||||
|| bundle[6] != 'e'
|
||||
|| bundle[7] != '\0' )
|
||||
{
|
||||
state_ = MALFORMED_BUNDLE_INVALID_ADDRESS_PATTERN_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
end_ = bundle + size;
|
||||
|
||||
timeTag_ = bundle + 8;
|
||||
|
||||
const char *p = timeTag_ + 8;
|
||||
|
||||
while( p < end_ ){
|
||||
if( p + osc::OSC_SIZEOF_INT32 > end_ )
|
||||
{
|
||||
state_ = MALFORMED_BUNDLE_TOO_SHORT_FOR_ELEMENT_SIZE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// treat element size as an unsigned int for the purposes of this calculation
|
||||
uint32 elementSize = ToUInt32( p );
|
||||
if( (elementSize & ((uint32)0x03)) != 0 )
|
||||
{
|
||||
state_ = MALFORMED_BUNDLE_ELEMENT_NOT_MULTIPLE_OF_4_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
p += osc::OSC_SIZEOF_INT32 + elementSize;
|
||||
if( p > end_ )
|
||||
{
|
||||
state_ = MALFORMED_BUNDLE_TOO_SHORT_FOR_ELEMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
++elementCount_;
|
||||
}
|
||||
|
||||
if( p != end_ )
|
||||
{
|
||||
state_ = MALFORMED_BUNDLE_CONTENT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedBundle::TimeTag() const
|
||||
{
|
||||
return ToUInt64( timeTag_ );
|
||||
}
|
||||
|
||||
|
||||
} // namespace osc
|
||||
|
||||
+581
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
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 <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring> // size_t
|
||||
|
||||
#include "OscTypes.h"
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
|
||||
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, &state_) ) {}
|
||||
|
||||
ReceivedPacket( const char *contents, std::size_t size )
|
||||
: contents_( contents )
|
||||
, size_( ValidateSize( (osc_bundle_element_size_t)size, &state_ ) ) {}
|
||||
|
||||
#if !(defined(__x86_64__) || defined(_M_X64))
|
||||
ReceivedPacket( const char *contents, int size )
|
||||
: contents_( contents )
|
||||
, size_( ValidateSize( (osc_bundle_element_size_t)size, &state_ ) ) {}
|
||||
#endif
|
||||
|
||||
bool IsMessage() const { return !IsBundle(); }
|
||||
bool IsBundle() const;
|
||||
|
||||
osc_bundle_element_size_t Size() const { return size_; }
|
||||
const char *Contents() const { return contents_; }
|
||||
Errors State() const { return state_; }
|
||||
|
||||
private:
|
||||
const char *contents_;
|
||||
osc_bundle_element_size_t size_;
|
||||
Errors state_;
|
||||
|
||||
static osc_bundle_element_size_t ValidateSize( osc_bundle_element_size_t size, Errors * state )
|
||||
{
|
||||
// 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) )
|
||||
{
|
||||
*state = MALFORMED_PACKET_INVALID_SIZE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( size == 0 )
|
||||
{
|
||||
*state = MALFORMED_PACKET_ZERO_SIZE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
{
|
||||
*state = MALFORMED_PACKET_NOT_MULTIPLE_OF_4_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*state = SUCCESS;
|
||||
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(Errors & state) const;
|
||||
bool AsBoolUnchecked(Errors & state) 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(Errors & state) const;
|
||||
int32 AsInt32Unchecked() const;
|
||||
|
||||
bool IsFloat() const { return *typeTagPtr_ == FLOAT_TYPE_TAG; }
|
||||
float AsFloat(Errors & state) const;
|
||||
float AsFloatUnchecked() const;
|
||||
|
||||
bool IsChar() const { return *typeTagPtr_ == CHAR_TYPE_TAG; }
|
||||
char AsChar(Errors & state) const;
|
||||
char AsCharUnchecked() const;
|
||||
|
||||
bool IsRgbaColor() const { return *typeTagPtr_ == RGBA_COLOR_TYPE_TAG; }
|
||||
uint32 AsRgbaColor(Errors & state) const;
|
||||
uint32 AsRgbaColorUnchecked() const;
|
||||
|
||||
bool IsMidiMessage() const { return *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG; }
|
||||
uint32 AsMidiMessage(Errors & state) const;
|
||||
uint32 AsMidiMessageUnchecked() const;
|
||||
|
||||
bool IsInt64() const { return *typeTagPtr_ == INT64_TYPE_TAG; }
|
||||
int64 AsInt64(Errors & state) const;
|
||||
int64 AsInt64Unchecked() const;
|
||||
|
||||
bool IsTimeTag() const { return *typeTagPtr_ == TIME_TAG_TYPE_TAG; }
|
||||
uint64 AsTimeTag(Errors & state) const;
|
||||
uint64 AsTimeTagUnchecked() const;
|
||||
|
||||
bool IsDouble() const { return *typeTagPtr_ == DOUBLE_TYPE_TAG; }
|
||||
double AsDouble(Errors & state) const;
|
||||
double AsDoubleUnchecked() const;
|
||||
|
||||
bool IsString() const { return *typeTagPtr_ == STRING_TYPE_TAG; }
|
||||
const char* AsString(Errors & state) const;
|
||||
const char* AsStringUnchecked() const { return argumentPtr_; }
|
||||
|
||||
bool IsSymbol() const { return *typeTagPtr_ == SYMBOL_TYPE_TAG; }
|
||||
const char* AsSymbol(Errors & state) 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, Errors & state ) const;
|
||||
void AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size, Errors & state ) 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(Errors & state) 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 )
|
||||
, state_( SUCCESS ) {}
|
||||
|
||||
ReceivedMessageArgumentIterator p_, end_;
|
||||
Errors state_;
|
||||
|
||||
public:
|
||||
|
||||
Errors State() const { return state_; }
|
||||
|
||||
// end of stream
|
||||
bool Eos() const { return p_ == end_; }
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( bool& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs = (*p_++).AsBool(state_);
|
||||
}
|
||||
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() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs = (*p_++).AsInt32(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( float& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs = (*p_++).AsFloat(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( char& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs = (*p_++).AsChar(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs.value = (*p_++).AsRgbaColor(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs.value = (*p_++).AsMidiMessage(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( int64& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs = (*p_++).AsInt64(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( TimeTag& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs.value = (*p_++).AsTimeTag(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( double& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs = (*p_++).AsDouble(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( Blob& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*p_++).AsBlob( rhs.data, rhs.size, state_ );
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( const char*& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs = (*p_++).AsString(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( Symbol& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = MISSING_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs.value = (*p_++).AsSymbol(state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs )
|
||||
{
|
||||
(void) rhs; // suppress unused parameter warning
|
||||
|
||||
if( Eos() )
|
||||
{
|
||||
state_ = EXCESS_ARGUMENT_ERROR;
|
||||
}
|
||||
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 );
|
||||
|
||||
Errors State() const { return state_; }
|
||||
|
||||
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<uint32>(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_;
|
||||
Errors state_;
|
||||
};
|
||||
|
||||
|
||||
class ReceivedBundle{
|
||||
void Init( const char *message, osc_bundle_element_size_t size );
|
||||
public:
|
||||
explicit ReceivedBundle( const ReceivedPacket& packet );
|
||||
explicit ReceivedBundle( const ReceivedBundleElement& bundleElement );
|
||||
|
||||
Errors State() const { return state_; }
|
||||
|
||||
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_;
|
||||
Errors state_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H */
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
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;
|
||||
|
||||
const TCHAR * errorString(Errors value)
|
||||
{
|
||||
static const TCHAR * ERROR_STR[] = {
|
||||
TEXT("No Error"),
|
||||
|
||||
TEXT("Malformed packet"),
|
||||
TEXT("Malformed packet: invalid size"),
|
||||
TEXT("Malformed packet: zero size"),
|
||||
TEXT("Malformed packet: not multiple of 4"),
|
||||
|
||||
TEXT("Malformed message"),
|
||||
TEXT("Malformed message: invalid size"),
|
||||
TEXT("Malformed message: zero size"),
|
||||
TEXT("Malformed message: not multiple of 4"),
|
||||
TEXT("Malformed message: invalid blob size"),
|
||||
TEXT("Malformed message: unterminated address pattern"),
|
||||
TEXT("Malformed message: unterminated string"),
|
||||
TEXT("Malformed message: unterminated array"),
|
||||
TEXT("Malformed message: no type tags"),
|
||||
TEXT("Malformed message: unterminated type tags"),
|
||||
TEXT("Malformed message: argument exceed msg size"),
|
||||
TEXT("Malformed message: unknown type tag"),
|
||||
|
||||
TEXT("Malformed bundle"),
|
||||
TEXT("Malformed bundle: invalid size"),
|
||||
TEXT("Malformed bundle: invalid address pattern"),
|
||||
TEXT("Malformed bundle: too short"),
|
||||
TEXT("Malformed bundle: too short for element size"),
|
||||
TEXT("Malformed bundle: too short for element"),
|
||||
TEXT("Malformed bundle: not multiple of 4"),
|
||||
TEXT("Malformed bundle: element not multiple of 4"),
|
||||
TEXT("Malformed bundle: content"),
|
||||
|
||||
TEXT("Wrong argument type"),
|
||||
TEXT("Missing argument"),
|
||||
TEXT("Excess argument"),
|
||||
TEXT("Out of buffer memory"),
|
||||
};
|
||||
|
||||
static_assert((sizeof(ERROR_STR) / sizeof(*ERROR_STR) == ERRORS_COUNT), "Error messages mismatch");
|
||||
if(value < 0 || value >= ERRORS_COUNT)
|
||||
{
|
||||
check(false);
|
||||
return TEXT("Unknown error code");
|
||||
}
|
||||
return ERROR_STR[value];
|
||||
}
|
||||
|
||||
} // namespace osc
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
oscpack -- Open Sound Control (OSC) packet manipulation library
|
||||
http://www.rossbencina.com/code/oscpack
|
||||
|
||||
Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
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;
|
||||
|
||||
|
||||
enum Errors {
|
||||
SUCCESS = 0,
|
||||
|
||||
MALFORMED_PACKET_ERROR,
|
||||
MALFORMED_PACKET_INVALID_SIZE_ERROR,
|
||||
MALFORMED_PACKET_ZERO_SIZE_ERROR,
|
||||
MALFORMED_PACKET_NOT_MULTIPLE_OF_4_ERROR,
|
||||
|
||||
MALFORMED_MESSAGE_ERROR,
|
||||
MALFORMED_MESSAGE_INVALID_SIZE_ERROR,
|
||||
MALFORMED_MESSAGE_ZERO_SIZE_ERROR,
|
||||
MALFORMED_MESSAGE_NOT_MULTIPLE_OF_4_ERROR,
|
||||
MALFORMED_MESSAGE_INVALID_BLOB_SIZE_ERROR,
|
||||
MALFORMED_MESSAGE_UNTERMINATED_ADDRESS_PATTERN_ERROR,
|
||||
MALFORMED_MESSAGE_UNTERMINATED_STRING_ERROR,
|
||||
MALFORMED_MESSAGE_UNTERMINATED_ARRAY_ERROR,
|
||||
MALFORMED_MESSAGE_NO_TYPE_TAGS_ERROR,
|
||||
MALFORMED_MESSAGE_UNTERMINATED_TYPE_TAGS_ERROR,
|
||||
MALFORMED_MESSAGE_ARGUMENT_EXCEED_MSG_SIZE_ERROR,
|
||||
MALFORMED_MESSAGE_UNKNOWN_TYPE_TAG_ERROR,
|
||||
|
||||
MALFORMED_BUNDLE_ERROR,
|
||||
MALFORMED_BUNDLE_INVALID_SIZE_ERROR,
|
||||
MALFORMED_BUNDLE_INVALID_ADDRESS_PATTERN_ERROR,
|
||||
MALFORMED_BUNDLE_TOO_SHORT_ERROR,
|
||||
MALFORMED_BUNDLE_TOO_SHORT_FOR_ELEMENT_SIZE_ERROR,
|
||||
MALFORMED_BUNDLE_TOO_SHORT_FOR_ELEMENT_ERROR,
|
||||
MALFORMED_BUNDLE_NOT_MULTIPLE_OF_4_ERROR,
|
||||
MALFORMED_BUNDLE_ELEMENT_NOT_MULTIPLE_OF_4_ERROR,
|
||||
MALFORMED_BUNDLE_CONTENT_ERROR,
|
||||
|
||||
WRONG_ARGUMENT_TYPE_ERROR,
|
||||
MISSING_ARGUMENT_ERROR,
|
||||
EXCESS_ARGUMENT_ERROR,
|
||||
OUT_OF_BUFFER_MEMORY_ERROR,
|
||||
|
||||
ERRORS_COUNT,
|
||||
};
|
||||
|
||||
const TCHAR * errorString(Errors value);
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCTYPES_H */
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
// Target engine version. Used to enable compatibility code.
|
||||
#define OSC_ENGINE_VERSION 40800
|
||||
|
||||
#ifndef OSC_EDITOR_BUILD
|
||||
#error OSC_EDITOR_BUILD must be set by the OSC.Build.cs
|
||||
#endif
|
||||
|
||||
|
||||
/* Public Dependencies
|
||||
*****************************************************************************/
|
||||
|
||||
#include "Networking.h"
|
||||
|
||||
|
||||
/* Public Includes
|
||||
*****************************************************************************/
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscSettings.h"
|
||||
#include "OscDispatcher.h"
|
||||
|
||||
#if OSC_EDITOR_BUILD
|
||||
#include "Editor.h"
|
||||
#endif
|
||||
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#if OSC_EDITOR_BUILD
|
||||
_mustListen = !GIsEditor; // must not listen now if IsEditor (listen when PIE), else (Standalone Game) listen now
|
||||
FEditorDelegates::BeginPIE.AddRaw(this, &FOscModule::OnBeginPIE);
|
||||
FEditorDelegates::EndPIE.AddRaw(this, &FOscModule::OnEndPIE);
|
||||
#endif
|
||||
|
||||
_dispatcher = UOscDispatcher::Get();
|
||||
|
||||
HandleSettingsSaved();
|
||||
|
||||
// register settings
|
||||
auto settingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
||||
if(settingsModule)
|
||||
{
|
||||
auto settingsSection = settingsModule->RegisterSettings("Project", "Plugins", "OSC",
|
||||
LOCTEXT("OscSettingsName", "OSC"),
|
||||
LOCTEXT("OscSettingsDescription", "Configure the OSC plug-in."),
|
||||
GetMutableDefault<UOscSettings>()
|
||||
);
|
||||
|
||||
if (settingsSection.IsValid())
|
||||
{
|
||||
settingsSection->OnModified().BindRaw(this, &FOscModule::HandleSettingsSaved);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if OSC_EDITOR_BUILD
|
||||
UE_LOG(LogOSC, Warning, TEXT("Settings changed registration failed"));
|
||||
#endif
|
||||
}
|
||||
UE_LOG(LogOSC, Display, TEXT("Startup succeed"));
|
||||
}
|
||||
|
||||
virtual void ShutdownModule( ) override
|
||||
{
|
||||
UE_LOG(LogOSC, Display, TEXT("Shutdown"));
|
||||
|
||||
if(_dispatcher.IsValid())
|
||||
{
|
||||
auto settings = GetMutableDefault<UOscSettings>();
|
||||
settings->ClearKeyInputs(*_dispatcher);
|
||||
|
||||
_dispatcher->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleSettingsSaved()
|
||||
{
|
||||
if(!_dispatcher.IsValid())
|
||||
{
|
||||
UE_LOG(LogOSC, Warning, TEXT("Cannot update settings"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(LogOSC, Display, TEXT("Update settings"));
|
||||
|
||||
auto settings = GetMutableDefault<UOscSettings>();
|
||||
|
||||
// receive settings
|
||||
#if OSC_EDITOR_BUILD
|
||||
if(_mustListen)
|
||||
{
|
||||
Listen(settings);
|
||||
}
|
||||
#else
|
||||
Listen(settings);
|
||||
#endif
|
||||
|
||||
// send settings
|
||||
settings->InitSendTargets();
|
||||
|
||||
// input settings
|
||||
settings->UpdateKeyInputs(*_dispatcher);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Listen(UOscSettings * settings)
|
||||
{
|
||||
FIPv4Address receiveAddress(0);
|
||||
uint32_t receivePort;
|
||||
if(UOscSettings::Parse(settings->ReceiveFrom, &receiveAddress, &receivePort))
|
||||
{
|
||||
_dispatcher->Listen(receiveAddress, receivePort);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Error, TEXT("Fail to parse receive address: %s"), *settings->ReceiveFrom);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
#if OSC_EDITOR_BUILD
|
||||
void OnBeginPIE(bool isSimulating)
|
||||
{
|
||||
_mustListen = true;
|
||||
|
||||
check(_dispatcher.IsValid())
|
||||
auto settings = GetMutableDefault<UOscSettings>();
|
||||
Listen(settings);
|
||||
}
|
||||
|
||||
void OnEndPIE(bool isSimulating)
|
||||
{
|
||||
_mustListen = false;
|
||||
_dispatcher->Stop();
|
||||
}
|
||||
|
||||
bool _mustListen;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
TWeakObjectPtr<UOscDispatcher> _dispatcher;
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_MODULE(FOscModule, OSC);
|
||||
DEFINE_LOG_CATEGORY(LogOSC);
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "Osc.h"
|
||||
|
||||
#include "Engine.h"
|
||||
#include "ISettingsModule.h"
|
||||
#include "ISettingsSection.h"
|
||||
|
||||
#include "ModuleManager.h"
|
||||
#include "Sockets.h"
|
||||
#include "SocketSubsystem.h"
|
||||
#include "TaskGraphInterfaces.h"
|
||||
|
||||
#include "Runtime/Engine/Classes/Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Runtime/Networking/Public/Networking.h"
|
||||
#include "Runtime/Core/Public/Misc/Base64.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOSC, Log, All);
|
||||
@@ -0,0 +1,176 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
|
||||
#include "OscSettings.h"
|
||||
#include "OscDispatcher.h"
|
||||
|
||||
|
||||
UOscSettings::UOscSettings()
|
||||
: ReceiveFrom("8000"),
|
||||
_sendSocket(FUdpSocketBuilder(TEXT("OscSender")).Build())
|
||||
{
|
||||
SendTargets.Add(TEXT("127.0.0.1:8000"));
|
||||
}
|
||||
|
||||
UOscSettings::UOscSettings(FVTableHelper & helper)
|
||||
: _sendSocket(FUdpSocketBuilder(TEXT("OscSender")).Build())
|
||||
{
|
||||
// Does not need to be a valid object.
|
||||
}
|
||||
|
||||
void UOscSettings::InitSendTargets()
|
||||
{
|
||||
UE_LOG(LogOSC, Display, TEXT("Send targets cleared"));
|
||||
|
||||
FString addressStr, portStr;
|
||||
|
||||
_sendAddresses.Empty();
|
||||
_sendAddresses.Reserve(SendTargets.Num());
|
||||
|
||||
_sendAddressesIndex.Empty();
|
||||
_sendAddressesIndex.Reserve(SendTargets.Num());
|
||||
|
||||
for(int32 i=0, n=SendTargets.Num(); i!=n; ++i)
|
||||
{
|
||||
const auto result = AddSendTarget(SendTargets[i]);
|
||||
check(result == i);
|
||||
}
|
||||
}
|
||||
|
||||
int32 UOscSettings::GetOrAddSendTarget(const FString & ip_port)
|
||||
{
|
||||
const int32 * result = _sendAddressesIndex.Find(ip_port);
|
||||
if(result)
|
||||
{
|
||||
return *result;
|
||||
}
|
||||
|
||||
return AddSendTarget(ip_port);
|
||||
}
|
||||
|
||||
int32 UOscSettings::AddSendTarget(const FString & ip_port)
|
||||
{
|
||||
auto target = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
|
||||
|
||||
FIPv4Address address(0);
|
||||
uint32_t port;
|
||||
if(Parse(ip_port, &address, &port) && address != FIPv4Address::Any)
|
||||
{
|
||||
target->SetIp(address.Value);
|
||||
target->SetPort(port);
|
||||
UE_LOG(LogOSC, Display, TEXT("Send target added: %s"), *ip_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Error, TEXT("Fail to parse or invalid send target: %s"), *ip_port);
|
||||
}
|
||||
|
||||
const auto result = _sendAddresses.Num();
|
||||
_sendAddressesIndex.Emplace(ip_port, result);
|
||||
_sendAddresses.Emplace(target);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void SendImpl(FSocket *socket, const uint8 *buffer, int32 length, const FInternetAddr & target)
|
||||
{
|
||||
int32 bytesSent = 0;
|
||||
while(length > 0)
|
||||
{
|
||||
socket->SendTo(buffer, length, bytesSent, target);
|
||||
length -= bytesSent;
|
||||
buffer += bytesSent;
|
||||
}
|
||||
}
|
||||
|
||||
void UOscSettings::Send(const uint8 *buffer, int32 length, int32 targetIndex)
|
||||
{
|
||||
if(targetIndex == -1)
|
||||
{
|
||||
for(const auto & address : _sendAddresses)
|
||||
{
|
||||
SendImpl(&_sendSocket.Get(), buffer, length, *address);
|
||||
}
|
||||
|
||||
#if !NO_LOGGING
|
||||
// Log sent packet
|
||||
if(!LogOSC.IsSuppressed(ELogVerbosity::Verbose))
|
||||
{
|
||||
TArray<uint8> tmp;
|
||||
tmp.Append(buffer, length);
|
||||
const auto encoded = FBase64::Encode(tmp);
|
||||
UE_LOG(LogOSC, Verbose, TEXT("SentAll: %s"), *encoded);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if(targetIndex < _sendAddresses.Num())
|
||||
{
|
||||
SendImpl(&_sendSocket.Get(), buffer, length, *_sendAddresses[targetIndex]);
|
||||
|
||||
#if !NO_LOGGING
|
||||
// Log sent packet
|
||||
if(!LogOSC.IsSuppressed(ELogVerbosity::Verbose))
|
||||
{
|
||||
TArray<uint8> tmp;
|
||||
tmp.Append(buffer, length);
|
||||
const auto encoded = FBase64::Encode(tmp);
|
||||
const auto target = _sendAddresses[targetIndex]->ToString(true);
|
||||
UE_LOG(LogOSC, Verbose, TEXT("SentTo %s: %s"), *target, *encoded);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Error, TEXT("Cannot send OSC: invalid targetIndex %d"), targetIndex);
|
||||
}
|
||||
}
|
||||
|
||||
bool UOscSettings::Parse(const FString & ip_port, FIPv4Address * address, uint32_t * port)
|
||||
{
|
||||
if(ip_port.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FIPv4Address addressResult(0);
|
||||
uint32_t portResult;
|
||||
|
||||
int32 sep = -1;
|
||||
if(ip_port.FindChar(TEXT(':'), sep))
|
||||
{
|
||||
const auto ip = ip_port.Left(sep).Trim();
|
||||
if(!FIPv4Address::Parse(ip, addressResult))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
portResult = FCString::Atoi(&ip_port.GetCharArray()[sep+1]);
|
||||
if(portResult == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*address = addressResult;
|
||||
*port = portResult;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UOscSettings::ClearKeyInputs(UOscDispatcher & dispatcher)
|
||||
{
|
||||
for(auto & receiver : _keyReceivers)
|
||||
{
|
||||
// Unregister here, not in the OscReceiverInputKey destructor
|
||||
// because it would crash at the application exit.
|
||||
dispatcher.UnregisterReceiver(&receiver);
|
||||
}
|
||||
_keyReceivers.Reset(0);
|
||||
}
|
||||
|
||||
void UOscSettings::UpdateKeyInputs(UOscDispatcher & dispatcher)
|
||||
{
|
||||
ClearKeyInputs(dispatcher);
|
||||
for(const auto & address : Inputs)
|
||||
{
|
||||
_keyReceivers.Emplace(address);
|
||||
dispatcher.RegisterReceiver(&_keyReceivers.Last());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include "OscReceiverInputKey.h"
|
||||
|
||||
#include "OscSettings.generated.h"
|
||||
|
||||
class UOscDispatcher;
|
||||
|
||||
|
||||
UCLASS(config=Engine)
|
||||
class UOscSettings : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/// Default constructor
|
||||
UOscSettings();
|
||||
|
||||
/// Hot reload constructor
|
||||
UOscSettings(FVTableHelper & helper);
|
||||
|
||||
/**
|
||||
* Specify the [address:]port to listen to.
|
||||
*
|
||||
* e.g.
|
||||
* - "8000" listen to messages from any sender on port 8000. [default]
|
||||
* - "224.0.0.100:8000" listen multi-cast messages of group 224.0.0.100 on port 8000.
|
||||
* - "192.168.0.1:8000" listen messages addressed specifically to 192.168.0.1 on port 8000, useful if there are several addresses for this machine.
|
||||
*/
|
||||
UPROPERTY(Config, EditAnywhere, Category=Receive)
|
||||
FString ReceiveFrom;
|
||||
|
||||
UPROPERTY(Config, EditAnywhere, Category=Send)
|
||||
TArray<FString> SendTargets;
|
||||
|
||||
/**
|
||||
* List of the messages treated as inputs.
|
||||
*
|
||||
* Every entry adds a key in the input mapping project setting.
|
||||
* e.g. "/position/x" -> "OSC_position_x"
|
||||
*/
|
||||
UPROPERTY(Config, EditAnywhere, Category=Input)
|
||||
TArray<FString> Inputs;
|
||||
|
||||
public:
|
||||
void InitSendTargets();
|
||||
|
||||
int32 GetOrAddSendTarget(const FString & ip_port);
|
||||
|
||||
void Send(const uint8 *buffer, int32 length, int32 targetIndex);
|
||||
|
||||
void ClearKeyInputs(UOscDispatcher & dispatcher);
|
||||
|
||||
void UpdateKeyInputs(UOscDispatcher & dispatcher);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Parse "8000" and "192.168.0.12" to IP and port values.
|
||||
* @return true if succeed
|
||||
*/
|
||||
static bool Parse(const FString & ip_port, FIPv4Address * address, uint32_t * port);
|
||||
|
||||
private:
|
||||
int32 AddSendTarget(const FString & ip_port);
|
||||
|
||||
private:
|
||||
TSharedRef<FSocket> _sendSocket;
|
||||
TArray<TSharedRef<FInternetAddr>> _sendAddresses;
|
||||
TMap<FString, int32> _sendAddressesIndex;
|
||||
TArray<OscReceiverInputKey> _keyReceivers;
|
||||
};
|
||||
@@ -0,0 +1,237 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscDispatcher.h"
|
||||
#include "OscReceiverInterface.h"
|
||||
|
||||
#include "oscpack/osc/OscReceivedElements.h"
|
||||
|
||||
|
||||
UOscDispatcher::UOscDispatcher()
|
||||
: _listening(FIPv4Address(0), 0),
|
||||
_socket(nullptr),
|
||||
_socketReceiver(nullptr),
|
||||
_pendingMessages(1024), // arbitrary max message count per frame
|
||||
_taskSpawned(0)
|
||||
{
|
||||
}
|
||||
|
||||
UOscDispatcher::UOscDispatcher(FVTableHelper & helper)
|
||||
: _pendingMessages(0)
|
||||
{
|
||||
// Does not need to be a valid object.
|
||||
}
|
||||
|
||||
UOscDispatcher * UOscDispatcher::Get()
|
||||
{
|
||||
return UOscDispatcher::StaticClass()->GetDefaultObject<UOscDispatcher>();
|
||||
}
|
||||
|
||||
void UOscDispatcher::Listen(FIPv4Address address, uint32_t port)
|
||||
{
|
||||
if(_listening != std::make_pair(address, port))
|
||||
{
|
||||
Stop();
|
||||
|
||||
FUdpSocketBuilder builder(TEXT("OscListener"));
|
||||
builder.BoundToPort(port);
|
||||
if(address.IsMulticastAddress())
|
||||
{
|
||||
builder.JoinedToGroup(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.BoundToAddress(address);
|
||||
}
|
||||
|
||||
_socket = builder.Build();
|
||||
if(_socket)
|
||||
{
|
||||
_socketReceiver = new FUdpSocketReceiver(_socket, FTimespan::FromMilliseconds(100), TEXT("OSCListener"));
|
||||
_socketReceiver->OnDataReceived().BindUObject(this, &UOscDispatcher::Callback);
|
||||
_socketReceiver->Start();
|
||||
|
||||
_listening = std::make_pair(address, 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"));
|
||||
|
||||
delete _socketReceiver;
|
||||
_socketReceiver = nullptr;
|
||||
|
||||
if(_socket)
|
||||
{
|
||||
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(_socket);
|
||||
_socket = nullptr;
|
||||
}
|
||||
|
||||
_listening = std::make_pair(FIPv4Address(0), 0);
|
||||
}
|
||||
|
||||
void UOscDispatcher::RegisterReceiver(IOscReceiverInterface * receiver)
|
||||
{
|
||||
FScopeLock ScopeLock(&_receiversMutex);
|
||||
_receivers.AddUnique(receiver);
|
||||
}
|
||||
|
||||
void UOscDispatcher::UnregisterReceiver(IOscReceiverInterface * receiver)
|
||||
{
|
||||
FScopeLock ScopeLock(&_receiversMutex);
|
||||
_receivers.Remove(receiver);
|
||||
}
|
||||
|
||||
static void SendMessage(TCircularQueue<std::tuple<FName, TArray<FOscDataElemStruct>, FIPv4Address>> & _pendingMessages,
|
||||
const osc::ReceivedMessage & message,
|
||||
const FIPv4Address & senderIp)
|
||||
{
|
||||
if(message.State() != osc::SUCCESS)
|
||||
{
|
||||
UE_LOG(LogOSC, Warning, TEXT("OSC Received Message Error: %s"), osc::errorString(message.State()));
|
||||
return;
|
||||
}
|
||||
const FName address(message.AddressPattern());
|
||||
|
||||
TArray<FOscDataElemStruct> 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())
|
||||
{
|
||||
osc::Errors error = osc::SUCCESS;
|
||||
elem.SetBool(it->AsBoolUnchecked(error));
|
||||
check(error == osc::SUCCESS);
|
||||
}
|
||||
else if(it->IsString())
|
||||
{
|
||||
elem.SetString(FName(it->AsStringUnchecked()));
|
||||
}
|
||||
data.Add(elem);
|
||||
}
|
||||
|
||||
// save it in pending messages
|
||||
const auto added = _pendingMessages.Enqueue(std::make_tuple(address, data, senderIp));
|
||||
|
||||
// the circular buffer may be full.
|
||||
if(!added)
|
||||
{
|
||||
UE_LOG(LogOSC, Warning, TEXT("Circular Buffer Full: Message Ignored"));
|
||||
}
|
||||
}
|
||||
|
||||
static void SendBundle(TCircularQueue<std::tuple<FName, TArray<FOscDataElemStruct>, FIPv4Address>> & _pendingMessages,
|
||||
const osc::ReceivedBundle & bundle,
|
||||
const FIPv4Address & senderIp)
|
||||
{
|
||||
if(bundle.State() != osc::SUCCESS)
|
||||
{
|
||||
UE_LOG(LogOSC, Warning, TEXT("OSC Received Bundle Error: %s"), osc::errorString(bundle.State()));
|
||||
return;
|
||||
}
|
||||
|
||||
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), senderIp);
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessage(_pendingMessages, osc::ReceivedMessage(*it), senderIp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOscDispatcher::Callback(const FArrayReaderPtr& data, const FIPv4Endpoint& endpoint)
|
||||
{
|
||||
const osc::ReceivedPacket packet((const char *)data->GetData(), data->Num());
|
||||
if(packet.State() != osc::SUCCESS)
|
||||
{
|
||||
UE_LOG(LogOSC, Warning, TEXT("OSC Received Packet Error: %s"), osc::errorString(packet.State()));
|
||||
return;
|
||||
}
|
||||
|
||||
if(packet.IsBundle())
|
||||
{
|
||||
SendBundle(_pendingMessages, osc::ReceivedBundle(packet), endpoint.Address);
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessage(_pendingMessages, osc::ReceivedMessage(packet), endpoint.Address);
|
||||
}
|
||||
|
||||
// Set a single callback in the main thread per frame.
|
||||
if(!_pendingMessages.IsEmpty() && !FPlatformAtomics::InterlockedCompareExchange(&_taskSpawned, 1, 0))
|
||||
{
|
||||
check(_taskSpawned == 1);
|
||||
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(
|
||||
FSimpleDelegateGraphTask::FDelegate::CreateUObject(this, &UOscDispatcher::CallbackMainThread),
|
||||
TStatId(),
|
||||
nullptr,
|
||||
ENamedThreads::GameThread);
|
||||
}
|
||||
|
||||
#if !NO_LOGGING
|
||||
// Log received packet
|
||||
if(!LogOSC.IsSuppressed(ELogVerbosity::Verbose))
|
||||
{
|
||||
const auto encoded = FBase64::Encode(*data);
|
||||
UE_LOG(LogOSC, Verbose, TEXT("Received: %s"), *encoded);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UOscDispatcher::CallbackMainThread()
|
||||
{
|
||||
// Release before dequeue.
|
||||
// If it was released after dequeue, when a message arrives after the while
|
||||
// loop and before the release, it would not be processed.
|
||||
check(_taskSpawned == 1);
|
||||
FPlatformAtomics::InterlockedCompareExchange(&_taskSpawned, 0, 1);
|
||||
|
||||
FScopeLock ScopeLock(&_receiversMutex);
|
||||
|
||||
std::tuple<FName, TArray<FOscDataElemStruct>, FIPv4Address> message;
|
||||
while(_pendingMessages.Dequeue(message))
|
||||
{
|
||||
const FIPv4Address & senderIp = std::get<2>(message);
|
||||
FString senderIpStr = FString::Printf(TEXT("%i.%i.%i.%i"), senderIp.A, senderIp.B, senderIp.C, senderIp.D);
|
||||
for(auto receiver : _receivers)
|
||||
{
|
||||
receiver->SendEvent(std::get<0>(message), std::get<1>(message), senderIpStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOscDispatcher::BeginDestroy()
|
||||
{
|
||||
Stop();
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataElemStruct.h"
|
||||
#include "OscDispatcher.generated.h"
|
||||
|
||||
struct IOscReceiverInterface;
|
||||
class FSocket;
|
||||
class FUdpSocketReceiver;
|
||||
|
||||
|
||||
/// Singleton that dispatches the OSC messages to listeners.
|
||||
UCLASS()
|
||||
class UOscDispatcher : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
friend class FOscModule;
|
||||
|
||||
void Listen(FIPv4Address address, uint32_t port);
|
||||
void Stop();
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
UOscDispatcher();
|
||||
|
||||
/// Hot reload constructor
|
||||
UOscDispatcher(FVTableHelper & helper);
|
||||
|
||||
/// 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<IOscReceiverInterface *> _receivers;
|
||||
std::pair<FIPv4Address, uint32_t> _listening;
|
||||
FSocket * _socket;
|
||||
FUdpSocketReceiver * _socketReceiver;
|
||||
TCircularQueue<std::tuple<FName, TArray<FOscDataElemStruct>, FIPv4Address>> _pendingMessages;
|
||||
int32 _taskSpawned;
|
||||
|
||||
/// Protects _receivers
|
||||
FCriticalSection _receiversMutex;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscReceiverActor.h"
|
||||
#include "OscDispatcher.h"
|
||||
|
||||
|
||||
AOscReceiverActor::AOscReceiverActor()
|
||||
: _listener(this)
|
||||
{
|
||||
auto instance = UOscDispatcher::Get();
|
||||
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
||||
{
|
||||
instance->RegisterReceiver(&_listener);
|
||||
|
||||
UE_LOG(LogOSC, Verbose, TEXT("Registering actor %s"), *GetName());
|
||||
}
|
||||
}
|
||||
|
||||
AOscReceiverActor::AOscReceiverActor(FVTableHelper & helper)
|
||||
: _listener(this)
|
||||
{
|
||||
// Does not need to be a valid object.
|
||||
}
|
||||
|
||||
void AOscReceiverActor::BeginDestroy()
|
||||
{
|
||||
auto instance = UOscDispatcher::Get();
|
||||
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
||||
{
|
||||
instance->UnregisterReceiver(&_listener);
|
||||
|
||||
UE_LOG(LogOSC, Verbose, TEXT("Unregistering actor %s"), *GetName());
|
||||
}
|
||||
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "OscReceiverInterface.h"
|
||||
#include "OscDataElemStruct.h"
|
||||
#include "OscReceiverActor.generated.h"
|
||||
|
||||
|
||||
UCLASS(ClassGroup=OSC)
|
||||
class AOscReceiverActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category=OSC)
|
||||
FString AddressFilter;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category=OSC)
|
||||
void OnOscReceived(const FName & Address, const TArray<FOscDataElemStruct> & Data, const FString & SenderIp);
|
||||
|
||||
public:
|
||||
|
||||
AOscReceiverActor();
|
||||
|
||||
/// Hot reload constructor
|
||||
AOscReceiverActor(FVTableHelper & helper);
|
||||
|
||||
const FString & GetAddressFilter() const
|
||||
{
|
||||
return AddressFilter;
|
||||
}
|
||||
|
||||
void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data, const FString & SenderIp)
|
||||
{
|
||||
OnOscReceived(Address, Data, SenderIp);
|
||||
}
|
||||
|
||||
private:
|
||||
void BeginDestroy() override;
|
||||
|
||||
private:
|
||||
BasicOscReceiver<AOscReceiverActor> _listener;
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscReceiverComponent.h"
|
||||
#include "OscDispatcher.h"
|
||||
|
||||
|
||||
UOscReceiverComponent::UOscReceiverComponent()
|
||||
: _listener(this)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
UOscReceiverComponent::UOscReceiverComponent(FVTableHelper & helper)
|
||||
: _listener(this)
|
||||
{
|
||||
// Does not need to be a valid object.
|
||||
}
|
||||
|
||||
void UOscReceiverComponent::OnRegister()
|
||||
{
|
||||
Super::OnRegister();
|
||||
|
||||
auto instance = UOscDispatcher::Get();
|
||||
if (instance &&
|
||||
!HasAnyFlags(RF_ClassDefaultObject) &&
|
||||
(!GetOwner() || !GetOwner()->HasAnyFlags(RF_ClassDefaultObject)))
|
||||
{
|
||||
instance->RegisterReceiver(&_listener);
|
||||
|
||||
if(GetOwner())
|
||||
{
|
||||
UE_LOG(LogOSC, Verbose, TEXT("Registering component %s of %s"), *GetName(), *GetOwner()->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Verbose, TEXT("Registering component %s"), *GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOscReceiverComponent::OnUnregister()
|
||||
{
|
||||
auto instance = UOscDispatcher::Get();
|
||||
if (instance &&
|
||||
!HasAnyFlags(RF_ClassDefaultObject) &&
|
||||
(!GetOwner() || !GetOwner()->HasAnyFlags(RF_ClassDefaultObject)))
|
||||
{
|
||||
instance->UnregisterReceiver(&_listener);
|
||||
|
||||
if(GetOwner())
|
||||
{
|
||||
UE_LOG(LogOSC, Verbose, TEXT("Unregistering component %s of %s"), *GetName(), *GetOwner()->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Verbose, TEXT("Unregistering component %s"), *GetName());
|
||||
}
|
||||
}
|
||||
|
||||
Super::OnUnregister();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "OscReceiverInterface.h"
|
||||
#include "OscDataElemStruct.h"
|
||||
#include "OscReceiverComponent.generated.h"
|
||||
|
||||
|
||||
// declare the OnOscReceived event type
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FComponentOscReceivedSignature, const FName &, Address, const TArray<FOscDataElemStruct> &, Data, const FString &, SenderIp);
|
||||
|
||||
|
||||
UCLASS(ClassGroup=OSC, meta = (BlueprintSpawnableComponent))
|
||||
class UOscReceiverComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category=OSC)
|
||||
FString AddressFilter;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category=OSC)
|
||||
FComponentOscReceivedSignature OnOscReceived;
|
||||
|
||||
public:
|
||||
|
||||
UOscReceiverComponent();
|
||||
|
||||
/// Hot reload constructor
|
||||
UOscReceiverComponent(FVTableHelper & helper);
|
||||
|
||||
const FString & GetAddressFilter() const
|
||||
{
|
||||
return AddressFilter;
|
||||
}
|
||||
|
||||
void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data, const FString & SenderIp)
|
||||
{
|
||||
OnOscReceived.Broadcast(Address, Data, SenderIp);
|
||||
}
|
||||
|
||||
private:
|
||||
void OnRegister() override;
|
||||
|
||||
void OnUnregister() override;
|
||||
|
||||
private:
|
||||
BasicOscReceiver<UOscReceiverComponent> _listener;
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscReceiverInputKey.h"
|
||||
#include "OscDispatcher.h"
|
||||
|
||||
#include "SlateBasics.h"
|
||||
|
||||
|
||||
OscReceiverInputKey::OscReceiverInputKey(const FString &address)
|
||||
: _address(address),
|
||||
_addressName(*address)
|
||||
{
|
||||
char buffer[512]; // address truncated after 500 chars.
|
||||
const auto length = sprintf(buffer, "OSC%.500s", StringCast<ANSICHAR>(*address).Get());
|
||||
|
||||
// replace '/' by '_' because '/' is not a valid FKey character.
|
||||
for(int i=0; i!=length; ++i)
|
||||
{
|
||||
if(buffer[i] == '/')
|
||||
{
|
||||
buffer[i] = '_';
|
||||
}
|
||||
}
|
||||
|
||||
_key = FName(buffer);
|
||||
|
||||
RegisterKey();
|
||||
}
|
||||
|
||||
void OscReceiverInputKey::RegisterKey() const
|
||||
{
|
||||
if(!_address.IsEmpty() && !EKeys::GetKeyDetails(_key).IsValid())
|
||||
{
|
||||
EKeys::AddKey(FKeyDetails(_key,
|
||||
FText::FromString(_key.ToString()),
|
||||
FKeyDetails::FloatAxis));
|
||||
}
|
||||
}
|
||||
|
||||
void OscReceiverInputKey::SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data, const FString & SenderIp)
|
||||
{
|
||||
if(Address != _addressName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto & app = FSlateApplication::Get();
|
||||
|
||||
const auto value = Data.Num() ? Data[0].GetValue<float>() : 0.f;
|
||||
|
||||
FKeyEvent keyEvent(_key, app.GetModifierKeys(), 0, false, 0, 0);
|
||||
if(value < 0.5f)
|
||||
{
|
||||
app.ProcessKeyDownEvent(keyEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
app.ProcessKeyUpEvent(keyEvent);
|
||||
}
|
||||
|
||||
FAnalogInputEvent axisEvent(_key, app.GetModifierKeys(), 0, false, 0, 0, value);
|
||||
app.ProcessAnalogInputEvent(axisEvent);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "InputCoreTypes.h"
|
||||
|
||||
#include "OscReceiverInterface.h"
|
||||
#include "OscDataElemStruct.h"
|
||||
|
||||
|
||||
class OscReceiverInputKey : public IOscReceiverInterface
|
||||
{
|
||||
public:
|
||||
OscReceiverInputKey(const FString & address);
|
||||
|
||||
OscReceiverInputKey(const OscReceiverInputKey &) = delete;
|
||||
OscReceiverInputKey& operator=(const OscReceiverInputKey &) = delete;
|
||||
|
||||
private:
|
||||
|
||||
/// Adds the custom input key to the Slate application.
|
||||
void RegisterKey() const;
|
||||
|
||||
const FString & GetAddressFilter() const override
|
||||
{
|
||||
return _address;
|
||||
}
|
||||
|
||||
void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data, const FString & SenderIp) override;
|
||||
|
||||
private:
|
||||
FString _address;
|
||||
FName _addressName;
|
||||
FKey _key;
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataElemStruct.h"
|
||||
|
||||
|
||||
/// Base class to receive OSC messages.
|
||||
struct IOscReceiverInterface
|
||||
{
|
||||
virtual const FString & GetAddressFilter() const = 0;
|
||||
virtual void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data, const FString & SenderIp) = 0;
|
||||
};
|
||||
|
||||
|
||||
/// Forward calls to an impl object.
|
||||
template <class T>
|
||||
struct BasicOscReceiver : IOscReceiverInterface
|
||||
{
|
||||
T * const _impl;
|
||||
|
||||
BasicOscReceiver(T * impl) : _impl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
const FString & GetAddressFilter() const final
|
||||
{
|
||||
return _impl->GetAddressFilter();
|
||||
}
|
||||
|
||||
void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data, const FString & SenderIp) final
|
||||
{
|
||||
_impl->SendEvent(Address, Data, SenderIp);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class OscDemoTarget : TargetRules
|
||||
{
|
||||
public OscDemoTarget(TargetInfo Target)
|
||||
{
|
||||
Type = TargetType.Game;
|
||||
}
|
||||
|
||||
//
|
||||
// TargetRules interface.
|
||||
//
|
||||
|
||||
public override void SetupBinaries(
|
||||
TargetInfo Target,
|
||||
ref List<UEBuildBinaryConfiguration> OutBuildBinaryConfigurations,
|
||||
ref List<string> OutExtraModuleNames
|
||||
)
|
||||
{
|
||||
OutExtraModuleNames.AddRange( new string[] { "OscDemo" } );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class OscDemo : ModuleRules
|
||||
{
|
||||
public OscDemo(TargetInfo Target)
|
||||
{
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { });
|
||||
|
||||
// Uncomment if you are using Slate UI
|
||||
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
|
||||
|
||||
// Uncomment if you are using online features
|
||||
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
|
||||
// if ((Target.Platform == UnrealTargetPlatform.Win32) || (Target.Platform == UnrealTargetPlatform.Win64))
|
||||
// {
|
||||
// if (UEBuildConfiguration.bCompileSteamOSS == true)
|
||||
// {
|
||||
// DynamicallyLoadedModuleNames.Add("OnlineSubsystemSteam");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "OscDemo.h"
|
||||
|
||||
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, OscDemo, "OscDemo" );
|
||||
@@ -0,0 +1,6 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine.h"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "OscDemo.h"
|
||||
#include "OscDemoGameMode.h"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/GameMode.h"
|
||||
#include "OscDemoGameMode.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class OSCDEMO_API AOscDemoGameMode : public AGameMode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class OscDemoEditorTarget : TargetRules
|
||||
{
|
||||
public OscDemoEditorTarget(TargetInfo Target)
|
||||
{
|
||||
Type = TargetType.Editor;
|
||||
}
|
||||
|
||||
//
|
||||
// TargetRules interface.
|
||||
//
|
||||
|
||||
public override void SetupBinaries(
|
||||
TargetInfo Target,
|
||||
ref List<UEBuildBinaryConfiguration> OutBuildBinaryConfigurations,
|
||||
ref List<string> OutExtraModuleNames
|
||||
)
|
||||
{
|
||||
OutExtraModuleNames.AddRange( new string[] { "OscDemo" } );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user