mirror of
https://github.com/storytold/UE4-OSC.git
synced 2026-10-09 00:09:43 +00:00
initial commit
This commit is contained in:
@@ -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,39 @@
|
||||
namespace UnrealBuildTool.Rules
|
||||
{
|
||||
public class OSC : ModuleRules
|
||||
{
|
||||
public OSC(TargetInfo Target)
|
||||
{
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Networking",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Sockets",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[] {
|
||||
"Settings",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"OSC/Private",
|
||||
"OSC/Private/Common",
|
||||
"OSC/Private/Receive",
|
||||
"OSC/Private/Send",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include "OscDataElemStruct.generated.h"
|
||||
|
||||
|
||||
USTRUCT()
|
||||
struct FOscDataElemStruct
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
int32 Type;
|
||||
|
||||
UPROPERTY()
|
||||
int64 Data;
|
||||
|
||||
FOscDataElemStruct() : Type(0), Data(0)
|
||||
{ }
|
||||
|
||||
enum
|
||||
{
|
||||
FLOAT = 0,
|
||||
INT = 1,
|
||||
BOOL = 2,
|
||||
STRING = 3,
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void SetFloat(double value)
|
||||
{
|
||||
static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the float value.");
|
||||
Type = FLOAT;
|
||||
Data = *reinterpret_cast<int64 *>(&value);
|
||||
assert(value == AsFloatValue());
|
||||
}
|
||||
|
||||
double AsFloatValue() const
|
||||
{
|
||||
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;
|
||||
assert(value == AsIntValue());
|
||||
}
|
||||
|
||||
int64 AsIntValue() const
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
|
||||
bool IsInt() const
|
||||
{
|
||||
return Type == INT;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void SetBool(bool value)
|
||||
{
|
||||
static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the bool value.");
|
||||
Type = BOOL;
|
||||
Data = value;
|
||||
assert(value == AsBoolValue());
|
||||
}
|
||||
|
||||
bool AsBoolValue() const
|
||||
{
|
||||
return Data != 0;
|
||||
}
|
||||
|
||||
bool IsBool() const
|
||||
{
|
||||
return Type == BOOL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void SetString(FName value)
|
||||
{
|
||||
static_assert(sizeof(value) <= sizeof(Data), "Too short to hold the string value.");
|
||||
Type = STRING;
|
||||
Data = *reinterpret_cast<int64*>(&value);
|
||||
assert(value == AsStringValue());
|
||||
}
|
||||
|
||||
FName AsStringValue() const
|
||||
{
|
||||
return *reinterpret_cast<const FName *>(&Data);
|
||||
}
|
||||
|
||||
bool IsString() const
|
||||
{
|
||||
return Type == STRING;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
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 = AsIntValue();
|
||||
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,106 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscFunctionLibrary.h"
|
||||
#include "oscpack/osc/OscOutboundPacketStream.h"
|
||||
|
||||
|
||||
UOscFunctionLibrary::UOscFunctionLibrary(const class FPostConstructInitializeProperties& PCIP)
|
||||
: Super(PCIP)
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static inline
|
||||
void PopValueImpl(const FOscDataStruct & input, FOscDataStruct & output, T & Value)
|
||||
{
|
||||
output = input;
|
||||
Value = output.Pop().GetValue<T>();
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PopBool(const FOscDataStruct & input, FOscDataStruct & output, bool & Value)
|
||||
{
|
||||
PopValueImpl(input, output, Value);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PopFloat(const FOscDataStruct & input, FOscDataStruct & output, float & Value)
|
||||
{
|
||||
PopValueImpl(input, output, Value);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PopInt(const FOscDataStruct & input, FOscDataStruct & output, int32 & Value)
|
||||
{
|
||||
PopValueImpl(input, output, Value);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PopString(const FOscDataStruct & input, FOscDataStruct & output, FName & Value)
|
||||
{
|
||||
PopValueImpl(input, output, Value);
|
||||
}
|
||||
|
||||
|
||||
void UOscFunctionLibrary::PushBool(FOscDataStruct input, bool Value, FOscDataStruct & output)
|
||||
{
|
||||
output = input;
|
||||
FOscDataElemStruct elem;
|
||||
elem.SetBool(Value);
|
||||
output.Queue.Add(elem);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PushFloat(FOscDataStruct input, float Value, FOscDataStruct & output)
|
||||
{
|
||||
output = input;
|
||||
FOscDataElemStruct elem;
|
||||
elem.SetFloat(Value);
|
||||
output.Queue.Add(elem);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PushInt(FOscDataStruct input, int32 Value, FOscDataStruct & output)
|
||||
{
|
||||
output = input;
|
||||
FOscDataElemStruct elem;
|
||||
elem.SetInt(Value);
|
||||
output.Queue.Add(elem);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::PushString(FOscDataStruct input, FName Value, FOscDataStruct & output)
|
||||
{
|
||||
output = input;
|
||||
FOscDataElemStruct elem;
|
||||
elem.SetString(Value);
|
||||
output.Queue.Add(elem);
|
||||
}
|
||||
|
||||
void UOscFunctionLibrary::SendOsc(const FString & Address, const FOscDataStruct & Data, int32 TargetIndex)
|
||||
{
|
||||
static_assert(sizeof(uint8) == sizeof(char), "Cannot cast uint8 to char");
|
||||
uint8 buffer[1024];
|
||||
|
||||
char oscAddress[256];
|
||||
strcpy(oscAddress, TCHAR_TO_ANSI(*Address));
|
||||
|
||||
osc::OutboundPacketStream output((char *)buffer, sizeof(buffer));
|
||||
output << osc::BeginMessage(oscAddress);
|
||||
for(int32 i=Data.Index, n=Data.Queue.Num(); i<n; ++i)
|
||||
{
|
||||
const auto & elem = Data.Queue[i];
|
||||
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();
|
||||
}
|
||||
}
|
||||
output << osc::EndMessage;
|
||||
|
||||
assert(buffer = output.Data());
|
||||
GetMutableDefault<UOscSettings>()->Send(buffer, output.Size(), TargetIndex);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataStruct.h"
|
||||
#include "OscFunctionLibrary.generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UOscFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
/// Get the next value from an OSC message as a boolean.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PopBool(const FOscDataStruct & input, FOscDataStruct & output, bool & Value);
|
||||
|
||||
/// Get the next value from an OSC message as a floating point.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PopFloat(const FOscDataStruct & input, FOscDataStruct & output, float & Value);
|
||||
|
||||
/// Get the next value from an OSC message as a integer.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PopInt(const FOscDataStruct & input, FOscDataStruct & output, int32 & Value);
|
||||
|
||||
/// Get the next value from an OSC message as a string.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PopString(const FOscDataStruct & input, FOscDataStruct & output, FName & Value);
|
||||
|
||||
|
||||
/// Add a boolean value to an OSC message.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PushBool(FOscDataStruct input, bool Value, FOscDataStruct & output);
|
||||
|
||||
/// Add a floating point value to an OSC message.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PushFloat(FOscDataStruct input, float Value, FOscDataStruct & output);
|
||||
|
||||
/// Add a integer value to an OSC message.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PushInt(FOscDataStruct input, int32 Value, FOscDataStruct & output);
|
||||
|
||||
/// Add a string value to an OSC message.
|
||||
UFUNCTION(BlueprintPure, Category=OSC)
|
||||
static void PushString(FOscDataStruct input, FName Value, FOscDataStruct & output);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send an OSC message.
|
||||
* @param Address OSC address.
|
||||
* @param Data result of successive PushFloat/Int/String/etc.
|
||||
* @param Index index of the destination. (SendTarget list of the plugin settings)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category=OSC)
|
||||
static void SendOsc(const FString & Address, const FOscDataStruct & Data, int32 TargetIndex);
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
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 )
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::CheckForAvailableBundleSpace()
|
||||
{
|
||||
std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) + 16;
|
||||
|
||||
if( required > Capacity() )
|
||||
throw OutOfBufferMemoryException();
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern )
|
||||
{
|
||||
// plus 4 for at least four bytes of type tag
|
||||
std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0)
|
||||
+ RoundUp4(std::strlen(addressPattern) + 1) + 4;
|
||||
|
||||
if( required > Capacity() )
|
||||
throw OutOfBufferMemoryException();
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::CheckForAvailableArgumentSpace( std::size_t argumentLength )
|
||||
{
|
||||
// plus three for extra type tag, comma and null terminator
|
||||
std::size_t required = (argumentCurrent_ - data_) + argumentLength
|
||||
+ RoundUp4( (end_ - typeTagsCurrent_) + 3 );
|
||||
|
||||
if( required > Capacity() )
|
||||
throw OutOfBufferMemoryException();
|
||||
}
|
||||
|
||||
|
||||
void OutboundPacketStream::Clear()
|
||||
{
|
||||
typeTagsCurrent_ = end_;
|
||||
messageCursor_ = data_;
|
||||
argumentCurrent_ = data_;
|
||||
elementSizePtr_ = 0;
|
||||
messageIsInProgress_ = false;
|
||||
}
|
||||
|
||||
|
||||
std::size_t OutboundPacketStream::Capacity() const
|
||||
{
|
||||
return end_ - data_;
|
||||
}
|
||||
|
||||
|
||||
std::size_t OutboundPacketStream::Size() const
|
||||
{
|
||||
std::size_t result = argumentCurrent_ - data_;
|
||||
if( IsMessageInProgress() ){
|
||||
// account for the length of the type tag string. the total type tag
|
||||
// includes an initial comma, plus at least one terminating \0
|
||||
result += RoundUp4( (end_ - typeTagsCurrent_) + 2 );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const char *OutboundPacketStream::Data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
|
||||
bool OutboundPacketStream::IsReady() const
|
||||
{
|
||||
return (!IsMessageInProgress() && !IsBundleInProgress());
|
||||
}
|
||||
|
||||
|
||||
bool OutboundPacketStream::IsMessageInProgress() const
|
||||
{
|
||||
return messageIsInProgress_;
|
||||
}
|
||||
|
||||
|
||||
bool OutboundPacketStream::IsBundleInProgress() const
|
||||
{
|
||||
return (elementSizePtr_ != 0);
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs )
|
||||
{
|
||||
if( IsMessageInProgress() )
|
||||
throw MessageInProgressException();
|
||||
|
||||
CheckForAvailableBundleSpace();
|
||||
|
||||
messageCursor_ = BeginElement( messageCursor_ );
|
||||
|
||||
std::memcpy( messageCursor_, "#bundle\0", 8 );
|
||||
FromUInt64( messageCursor_ + 8, rhs.timeTag );
|
||||
|
||||
messageCursor_ += 16;
|
||||
argumentCurrent_ = messageCursor_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
|
||||
if( !IsBundleInProgress() )
|
||||
throw BundleNotInProgressException();
|
||||
if( IsMessageInProgress() )
|
||||
throw MessageInProgressException();
|
||||
|
||||
EndElement( messageCursor_ );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs )
|
||||
{
|
||||
if( IsMessageInProgress() )
|
||||
throw MessageInProgressException();
|
||||
|
||||
CheckForAvailableMessageSpace( rhs.addressPattern );
|
||||
|
||||
messageCursor_ = BeginElement( messageCursor_ );
|
||||
|
||||
std::strcpy( messageCursor_, rhs.addressPattern );
|
||||
std::size_t rhsLength = std::strlen(rhs.addressPattern);
|
||||
messageCursor_ += rhsLength + 1;
|
||||
|
||||
// zero pad to 4-byte boundary
|
||||
std::size_t i = rhsLength + 1;
|
||||
while( i & 0x3 ){
|
||||
*messageCursor_++ = '\0';
|
||||
++i;
|
||||
}
|
||||
|
||||
argumentCurrent_ = messageCursor_;
|
||||
typeTagsCurrent_ = end_;
|
||||
|
||||
messageIsInProgress_ = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
|
||||
if( !IsMessageInProgress() )
|
||||
throw MessageNotInProgressException();
|
||||
|
||||
std::size_t typeTagsCount = end_ - typeTagsCurrent_;
|
||||
|
||||
if( typeTagsCount ){
|
||||
|
||||
char *tempTypeTags = (char*)alloca(typeTagsCount);
|
||||
std::memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount );
|
||||
|
||||
// slot size includes comma and null terminator
|
||||
std::size_t typeTagSlotSize = RoundUp4( typeTagsCount + 2 );
|
||||
|
||||
std::size_t argumentsSize = argumentCurrent_ - messageCursor_;
|
||||
|
||||
std::memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize );
|
||||
|
||||
messageCursor_[0] = ',';
|
||||
// copy type tags in reverse (really forward) order
|
||||
for( std::size_t i=0; i < typeTagsCount; ++i )
|
||||
messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ];
|
||||
|
||||
char *p = messageCursor_ + 1 + typeTagsCount;
|
||||
for( std::size_t i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i )
|
||||
*p++ = '\0';
|
||||
|
||||
typeTagsCurrent_ = end_;
|
||||
|
||||
// advance messageCursor_ for next message
|
||||
messageCursor_ += typeTagSlotSize + argumentsSize;
|
||||
|
||||
}else{
|
||||
// send an empty type tags string
|
||||
std::memcpy( messageCursor_, ",\0\0\0", 4 );
|
||||
|
||||
// advance messageCursor_ for next message
|
||||
messageCursor_ += 4;
|
||||
}
|
||||
|
||||
argumentCurrent_ = messageCursor_;
|
||||
|
||||
EndElement( messageCursor_ );
|
||||
|
||||
messageIsInProgress_ = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = NIL_TYPE_TAG;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = INFINITUM_TYPE_TAG;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = INT32_TYPE_TAG;
|
||||
FromInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( float rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = FLOAT_TYPE_TAG;
|
||||
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
float f;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.f = rhs;
|
||||
|
||||
argumentCurrent_[3] = u.c[0];
|
||||
argumentCurrent_[2] = u.c[1];
|
||||
argumentCurrent_[1] = u.c[2];
|
||||
argumentCurrent_[0] = u.c[3];
|
||||
#else
|
||||
*reinterpret_cast<float*>(argumentCurrent_) = rhs;
|
||||
#endif
|
||||
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( char rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = CHAR_TYPE_TAG;
|
||||
FromInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG;
|
||||
FromUInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(4);
|
||||
|
||||
*(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG;
|
||||
FromUInt32( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(8);
|
||||
|
||||
*(--typeTagsCurrent_) = INT64_TYPE_TAG;
|
||||
FromInt64( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 8;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(8);
|
||||
|
||||
*(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG;
|
||||
FromUInt64( argumentCurrent_, rhs );
|
||||
argumentCurrent_ += 8;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( double rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace(8);
|
||||
|
||||
*(--typeTagsCurrent_) = DOUBLE_TYPE_TAG;
|
||||
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
double f;
|
||||
char c[8];
|
||||
} u;
|
||||
|
||||
u.f = rhs;
|
||||
|
||||
argumentCurrent_[7] = u.c[0];
|
||||
argumentCurrent_[6] = u.c[1];
|
||||
argumentCurrent_[5] = u.c[2];
|
||||
argumentCurrent_[4] = u.c[3];
|
||||
argumentCurrent_[3] = u.c[4];
|
||||
argumentCurrent_[2] = u.c[5];
|
||||
argumentCurrent_[1] = u.c[6];
|
||||
argumentCurrent_[0] = u.c[7];
|
||||
#else
|
||||
*reinterpret_cast<double*>(argumentCurrent_) = rhs;
|
||||
#endif
|
||||
|
||||
argumentCurrent_ += 8;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) );
|
||||
|
||||
*(--typeTagsCurrent_) = STRING_TYPE_TAG;
|
||||
std::strcpy( argumentCurrent_, rhs );
|
||||
std::size_t rhsLength = std::strlen(rhs);
|
||||
argumentCurrent_ += rhsLength + 1;
|
||||
|
||||
// zero pad to 4-byte boundary
|
||||
std::size_t i = rhsLength + 1;
|
||||
while( i & 0x3 ){
|
||||
*argumentCurrent_++ = '\0';
|
||||
++i;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) );
|
||||
|
||||
*(--typeTagsCurrent_) = SYMBOL_TYPE_TAG;
|
||||
std::strcpy( argumentCurrent_, rhs );
|
||||
std::size_t rhsLength = std::strlen(rhs);
|
||||
argumentCurrent_ += rhsLength + 1;
|
||||
|
||||
// zero pad to 4-byte boundary
|
||||
std::size_t i = rhsLength + 1;
|
||||
while( i & 0x3 ){
|
||||
*argumentCurrent_++ = '\0';
|
||||
++i;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs )
|
||||
{
|
||||
CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) );
|
||||
|
||||
*(--typeTagsCurrent_) = BLOB_TYPE_TAG;
|
||||
FromUInt32( argumentCurrent_, rhs.size );
|
||||
argumentCurrent_ += 4;
|
||||
|
||||
std::memcpy( argumentCurrent_, rhs.data, rhs.size );
|
||||
argumentCurrent_ += rhs.size;
|
||||
|
||||
// zero pad to 4-byte boundary
|
||||
unsigned long i = rhs.size;
|
||||
while( i & 0x3 ){
|
||||
*argumentCurrent_++ = '\0';
|
||||
++i;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayInitiator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = ARRAY_BEGIN_TYPE_TAG;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayTerminator& rhs )
|
||||
{
|
||||
(void) rhs;
|
||||
CheckForAvailableArgumentSpace(0);
|
||||
|
||||
*(--typeTagsCurrent_) = ARRAY_END_TYPE_TAG;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
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"
|
||||
#include "OscException.h"
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
class OutOfBufferMemoryException : public Exception{
|
||||
public:
|
||||
OutOfBufferMemoryException( const char *w="out of buffer memory" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class BundleNotInProgressException : public Exception{
|
||||
public:
|
||||
BundleNotInProgressException(
|
||||
const char *w="call to EndBundle when bundle is not in progress" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MessageInProgressException : public Exception{
|
||||
public:
|
||||
MessageInProgressException(
|
||||
const char *w="opening or closing bundle or message while message is in progress" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MessageNotInProgressException : public Exception{
|
||||
public:
|
||||
MessageNotInProgressException(
|
||||
const char *w="call to EndMessage when message is not in progress" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
|
||||
class OutboundPacketStream{
|
||||
public:
|
||||
OutboundPacketStream( char *buffer, std::size_t capacity );
|
||||
~OutboundPacketStream();
|
||||
|
||||
void Clear();
|
||||
|
||||
std::size_t Capacity() const;
|
||||
|
||||
// invariant: size() is valid even while building a message.
|
||||
std::size_t Size() const;
|
||||
|
||||
const char *Data() const;
|
||||
|
||||
// indicates that all messages have been closed with a matching EndMessage
|
||||
// and all bundles have been closed with a matching EndBundle
|
||||
bool IsReady() const;
|
||||
|
||||
bool IsMessageInProgress() const;
|
||||
bool IsBundleInProgress() const;
|
||||
|
||||
OutboundPacketStream& operator<<( const BundleInitiator& rhs );
|
||||
OutboundPacketStream& operator<<( const BundleTerminator& rhs );
|
||||
|
||||
OutboundPacketStream& operator<<( const BeginMessage& rhs );
|
||||
OutboundPacketStream& operator<<( const MessageTerminator& rhs );
|
||||
|
||||
OutboundPacketStream& operator<<( bool rhs );
|
||||
OutboundPacketStream& operator<<( const NilType& rhs );
|
||||
OutboundPacketStream& operator<<( const InfinitumType& rhs );
|
||||
OutboundPacketStream& operator<<( int32 rhs );
|
||||
|
||||
#if !(defined(__x86_64__) || defined(_M_X64))
|
||||
OutboundPacketStream& operator<<( int rhs )
|
||||
{ *this << (int32)rhs; return *this; }
|
||||
#endif
|
||||
|
||||
OutboundPacketStream& operator<<( float rhs );
|
||||
OutboundPacketStream& operator<<( char rhs );
|
||||
OutboundPacketStream& operator<<( const RgbaColor& rhs );
|
||||
OutboundPacketStream& operator<<( const MidiMessage& rhs );
|
||||
OutboundPacketStream& operator<<( int64 rhs );
|
||||
OutboundPacketStream& operator<<( const TimeTag& rhs );
|
||||
OutboundPacketStream& operator<<( double rhs );
|
||||
OutboundPacketStream& operator<<( const char* rhs );
|
||||
OutboundPacketStream& operator<<( const Symbol& rhs );
|
||||
OutboundPacketStream& operator<<( const Blob& rhs );
|
||||
|
||||
OutboundPacketStream& operator<<( const ArrayInitiator& rhs );
|
||||
OutboundPacketStream& operator<<( const ArrayTerminator& rhs );
|
||||
|
||||
private:
|
||||
|
||||
char *BeginElement( char *beginPtr );
|
||||
void EndElement( char *endPtr );
|
||||
|
||||
bool ElementSizeSlotRequired() const;
|
||||
void CheckForAvailableBundleSpace();
|
||||
void CheckForAvailableMessageSpace( const char *addressPattern );
|
||||
void CheckForAvailableArgumentSpace( std::size_t argumentLength );
|
||||
|
||||
char *data_;
|
||||
char *end_;
|
||||
|
||||
char *typeTagsCurrent_; // stored in reverse order
|
||||
char *messageCursor_;
|
||||
char *argumentCurrent_;
|
||||
|
||||
// elementSizePtr_ has two special values: 0 indicates that a bundle
|
||||
// isn't open, and elementSizePtr_==data_ indicates that a bundle is
|
||||
// open but that it doesn't have a size slot (ie the outermost bundle)
|
||||
uint32 *elementSizePtr_;
|
||||
|
||||
bool messageIsInProgress_;
|
||||
};
|
||||
|
||||
} // namespace osc
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H */
|
||||
@@ -0,0 +1,799 @@
|
||||
/*
|
||||
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() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == TRUE_TYPE_TAG )
|
||||
return true;
|
||||
else if( *typeTagPtr_ == FALSE_TYPE_TAG )
|
||||
return false;
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
bool ReceivedMessageArgument::AsBoolUnchecked() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == TRUE_TYPE_TAG )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int32 ReceivedMessageArgument::AsInt32() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == INT32_TYPE_TAG )
|
||||
return AsInt32Unchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
int32 ReceivedMessageArgument::AsInt32Unchecked() const
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
osc::int32 i;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.c[0] = argumentPtr_[3];
|
||||
u.c[1] = argumentPtr_[2];
|
||||
u.c[2] = argumentPtr_[1];
|
||||
u.c[3] = argumentPtr_[0];
|
||||
|
||||
return u.i;
|
||||
#else
|
||||
return *(int32*)argument_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
float ReceivedMessageArgument::AsFloat() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == FLOAT_TYPE_TAG )
|
||||
return AsFloatUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
float ReceivedMessageArgument::AsFloatUnchecked() const
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
float f;
|
||||
char c[4];
|
||||
} u;
|
||||
|
||||
u.c[0] = argumentPtr_[3];
|
||||
u.c[1] = argumentPtr_[2];
|
||||
u.c[2] = argumentPtr_[1];
|
||||
u.c[3] = argumentPtr_[0];
|
||||
|
||||
return u.f;
|
||||
#else
|
||||
return *(float*)argument_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
char ReceivedMessageArgument::AsChar() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == CHAR_TYPE_TAG )
|
||||
return AsCharUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
char ReceivedMessageArgument::AsCharUnchecked() const
|
||||
{
|
||||
return (char)ToInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsRgbaColor() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == RGBA_COLOR_TYPE_TAG )
|
||||
return AsRgbaColorUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const
|
||||
{
|
||||
return ToUInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsMidiMessage() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG )
|
||||
return AsMidiMessageUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const
|
||||
{
|
||||
return ToUInt32( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
int64 ReceivedMessageArgument::AsInt64() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == INT64_TYPE_TAG )
|
||||
return AsInt64Unchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
int64 ReceivedMessageArgument::AsInt64Unchecked() const
|
||||
{
|
||||
return ToInt64( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedMessageArgument::AsTimeTag() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == TIME_TAG_TYPE_TAG )
|
||||
return AsTimeTagUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const
|
||||
{
|
||||
return ToUInt64( argumentPtr_ );
|
||||
}
|
||||
|
||||
|
||||
double ReceivedMessageArgument::AsDouble() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == DOUBLE_TYPE_TAG )
|
||||
return AsDoubleUnchecked();
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
double ReceivedMessageArgument::AsDoubleUnchecked() const
|
||||
{
|
||||
#ifdef OSC_HOST_LITTLE_ENDIAN
|
||||
union{
|
||||
double d;
|
||||
char c[8];
|
||||
} u;
|
||||
|
||||
u.c[0] = argumentPtr_[7];
|
||||
u.c[1] = argumentPtr_[6];
|
||||
u.c[2] = argumentPtr_[5];
|
||||
u.c[3] = argumentPtr_[4];
|
||||
u.c[4] = argumentPtr_[3];
|
||||
u.c[5] = argumentPtr_[2];
|
||||
u.c[6] = argumentPtr_[1];
|
||||
u.c[7] = argumentPtr_[0];
|
||||
|
||||
return u.d;
|
||||
#else
|
||||
return *(double*)argument_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
const char* ReceivedMessageArgument::AsString() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == STRING_TYPE_TAG )
|
||||
return argumentPtr_;
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
const char* ReceivedMessageArgument::AsSymbol() const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == SYMBOL_TYPE_TAG )
|
||||
return argumentPtr_;
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
void ReceivedMessageArgument::AsBlob( const void*& data, osc_bundle_element_size_t& size ) const
|
||||
{
|
||||
if( !typeTagPtr_ )
|
||||
throw MissingArgumentException();
|
||||
else if( *typeTagPtr_ == BLOB_TYPE_TAG )
|
||||
AsBlobUnchecked( data, size );
|
||||
else
|
||||
throw WrongArgumentTypeException();
|
||||
}
|
||||
|
||||
|
||||
void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const
|
||||
{
|
||||
// read blob size as an unsigned int then validate
|
||||
osc_bundle_element_size_t sizeResult = (osc_bundle_element_size_t)ToUInt32( argumentPtr_ );
|
||||
if( !IsValidElementSizeValue(sizeResult) )
|
||||
throw MalformedMessageException("invalid blob size");
|
||||
|
||||
size = sizeResult;
|
||||
data = (void*)(argumentPtr_+ osc::OSC_SIZEOF_INT32);
|
||||
}
|
||||
|
||||
std::size_t ReceivedMessageArgument::ComputeArrayItemCount() const
|
||||
{
|
||||
// it is only valid to call ComputeArrayItemCount when the argument is the array start marker
|
||||
if( !IsArrayBegin() )
|
||||
throw WrongArgumentTypeException();
|
||||
|
||||
std::size_t result = 0;
|
||||
unsigned int level = 0;
|
||||
const char *typeTag = typeTagPtr_ + 1;
|
||||
|
||||
// iterate through all type tags. note that ReceivedMessage::Init
|
||||
// has already checked that the message is well formed.
|
||||
while( *typeTag ) {
|
||||
switch( *typeTag++ ) {
|
||||
case ARRAY_BEGIN_TYPE_TAG:
|
||||
level += 1;
|
||||
break;
|
||||
|
||||
case ARRAY_END_TYPE_TAG:
|
||||
if(level == 0)
|
||||
return result;
|
||||
level -= 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
if( level == 0 ) // only count items at level 0
|
||||
++result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ReceivedMessageArgumentIterator::Advance()
|
||||
{
|
||||
if( !value_.typeTagPtr_ )
|
||||
return;
|
||||
|
||||
switch( *value_.typeTagPtr_++ ){
|
||||
case '\0':
|
||||
// don't advance past end
|
||||
--value_.typeTagPtr_;
|
||||
break;
|
||||
|
||||
case TRUE_TYPE_TAG:
|
||||
case FALSE_TYPE_TAG:
|
||||
case NIL_TYPE_TAG:
|
||||
case INFINITUM_TYPE_TAG:
|
||||
|
||||
// zero length
|
||||
break;
|
||||
|
||||
case INT32_TYPE_TAG:
|
||||
case FLOAT_TYPE_TAG:
|
||||
case CHAR_TYPE_TAG:
|
||||
case RGBA_COLOR_TYPE_TAG:
|
||||
case MIDI_MESSAGE_TYPE_TAG:
|
||||
|
||||
value_.argumentPtr_ += 4;
|
||||
break;
|
||||
|
||||
case INT64_TYPE_TAG:
|
||||
case TIME_TAG_TYPE_TAG:
|
||||
case DOUBLE_TYPE_TAG:
|
||||
|
||||
value_.argumentPtr_ += 8;
|
||||
break;
|
||||
|
||||
case STRING_TYPE_TAG:
|
||||
case SYMBOL_TYPE_TAG:
|
||||
|
||||
// we use the unsafe function FindStr4End(char*) here because all of
|
||||
// the arguments have already been validated in
|
||||
// ReceivedMessage::Init() below.
|
||||
|
||||
value_.argumentPtr_ = FindStr4End( value_.argumentPtr_ );
|
||||
break;
|
||||
|
||||
case BLOB_TYPE_TAG:
|
||||
{
|
||||
// treat blob size as an unsigned int for the purposes of this calculation
|
||||
uint32 blobSize = ToUInt32( value_.argumentPtr_ );
|
||||
value_.argumentPtr_ = value_.argumentPtr_ + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize );
|
||||
}
|
||||
break;
|
||||
|
||||
case ARRAY_BEGIN_TYPE_TAG:
|
||||
case ARRAY_END_TYPE_TAG:
|
||||
|
||||
// [ Indicates the beginning of an array. The tags following are for
|
||||
// data in the Array until a close brace tag is reached.
|
||||
// ] Indicates the end of an array.
|
||||
|
||||
// zero length, don't advance argument ptr
|
||||
break;
|
||||
|
||||
default: // unknown type tag
|
||||
// don't advance
|
||||
--value_.typeTagPtr_;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet )
|
||||
: addressPattern_( packet.Contents() )
|
||||
{
|
||||
Init( packet.Contents(), packet.Size() );
|
||||
}
|
||||
|
||||
|
||||
ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement )
|
||||
: addressPattern_( bundleElement.Contents() )
|
||||
{
|
||||
Init( bundleElement.Contents(), bundleElement.Size() );
|
||||
}
|
||||
|
||||
|
||||
bool ReceivedMessage::AddressPatternIsUInt32() const
|
||||
{
|
||||
return (addressPattern_[0] == '\0');
|
||||
}
|
||||
|
||||
|
||||
uint32 ReceivedMessage::AddressPatternAsUInt32() const
|
||||
{
|
||||
return ToUInt32( addressPattern_ );
|
||||
}
|
||||
|
||||
|
||||
void ReceivedMessage::Init( const char *message, osc_bundle_element_size_t size )
|
||||
{
|
||||
if( !IsValidElementSizeValue(size) )
|
||||
throw MalformedMessageException( "invalid message size" );
|
||||
|
||||
if( size == 0 )
|
||||
throw MalformedMessageException( "zero length messages not permitted" );
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
throw MalformedMessageException( "message size must be multiple of four" );
|
||||
|
||||
const char *end = message + size;
|
||||
|
||||
typeTagsBegin_ = FindStr4End( addressPattern_, end );
|
||||
if( typeTagsBegin_ == 0 ){
|
||||
// address pattern was not terminated before end
|
||||
throw MalformedMessageException( "unterminated address pattern" );
|
||||
}
|
||||
|
||||
if( typeTagsBegin_ == end ){
|
||||
// message consists of only the address pattern - no arguments or type tags.
|
||||
typeTagsBegin_ = 0;
|
||||
typeTagsEnd_ = 0;
|
||||
arguments_ = 0;
|
||||
|
||||
}else{
|
||||
if( *typeTagsBegin_ != ',' )
|
||||
throw MalformedMessageException( "type tags not present" );
|
||||
|
||||
if( *(typeTagsBegin_ + 1) == '\0' ){
|
||||
// zero length type tags
|
||||
typeTagsBegin_ = 0;
|
||||
typeTagsEnd_ = 0;
|
||||
arguments_ = 0;
|
||||
|
||||
}else{
|
||||
// check that all arguments are present and well formed
|
||||
|
||||
arguments_ = FindStr4End( typeTagsBegin_, end );
|
||||
if( arguments_ == 0 ){
|
||||
throw MalformedMessageException( "type tags were not terminated before end of message" );
|
||||
}
|
||||
|
||||
++typeTagsBegin_; // advance past initial ','
|
||||
|
||||
const char *typeTag = typeTagsBegin_;
|
||||
const char *argument = arguments_;
|
||||
unsigned int arrayLevel = 0;
|
||||
|
||||
do{
|
||||
switch( *typeTag ){
|
||||
case TRUE_TYPE_TAG:
|
||||
case FALSE_TYPE_TAG:
|
||||
case NIL_TYPE_TAG:
|
||||
case INFINITUM_TYPE_TAG:
|
||||
// zero length
|
||||
break;
|
||||
|
||||
// [ Indicates the beginning of an array. The tags following are for
|
||||
// data in the Array until a close brace tag is reached.
|
||||
// ] Indicates the end of an array.
|
||||
case ARRAY_BEGIN_TYPE_TAG:
|
||||
++arrayLevel;
|
||||
// (zero length argument data)
|
||||
break;
|
||||
|
||||
case ARRAY_END_TYPE_TAG:
|
||||
--arrayLevel;
|
||||
// (zero length argument data)
|
||||
break;
|
||||
|
||||
case INT32_TYPE_TAG:
|
||||
case FLOAT_TYPE_TAG:
|
||||
case CHAR_TYPE_TAG:
|
||||
case RGBA_COLOR_TYPE_TAG:
|
||||
case MIDI_MESSAGE_TYPE_TAG:
|
||||
|
||||
if( argument == end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
argument += 4;
|
||||
if( argument > end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
break;
|
||||
|
||||
case INT64_TYPE_TAG:
|
||||
case TIME_TAG_TYPE_TAG:
|
||||
case DOUBLE_TYPE_TAG:
|
||||
|
||||
if( argument == end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
argument += 8;
|
||||
if( argument > end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
break;
|
||||
|
||||
case STRING_TYPE_TAG:
|
||||
case SYMBOL_TYPE_TAG:
|
||||
|
||||
if( argument == end )
|
||||
throw MalformedMessageException( "arguments exceed message size" );
|
||||
argument = FindStr4End( argument, end );
|
||||
if( argument == 0 )
|
||||
throw MalformedMessageException( "unterminated string argument" );
|
||||
break;
|
||||
|
||||
case BLOB_TYPE_TAG:
|
||||
{
|
||||
if( argument + osc::OSC_SIZEOF_INT32 > end )
|
||||
MalformedMessageException( "arguments exceed message size" );
|
||||
|
||||
// treat blob size as an unsigned int for the purposes of this calculation
|
||||
uint32 blobSize = ToUInt32( argument );
|
||||
argument = argument + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize );
|
||||
if( argument > end )
|
||||
MalformedMessageException( "arguments exceed message size" );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw MalformedMessageException( "unknown type tag" );
|
||||
}
|
||||
|
||||
}while( *++typeTag != '\0' );
|
||||
typeTagsEnd_ = typeTag;
|
||||
|
||||
if( arrayLevel != 0 )
|
||||
throw MalformedMessageException( "array was not terminated before end of message (expected ']' end of array tag)" );
|
||||
}
|
||||
|
||||
// These invariants should be guaranteed by the above code.
|
||||
// we depend on them in the implementation of ArgumentCount()
|
||||
#ifndef NDEBUG
|
||||
std::ptrdiff_t argumentCount = typeTagsEnd_ - typeTagsBegin_;
|
||||
assert( argumentCount >= 0 );
|
||||
assert( argumentCount <= OSC_INT32_MAX );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet )
|
||||
: elementCount_( 0 )
|
||||
{
|
||||
Init( packet.Contents(), packet.Size() );
|
||||
}
|
||||
|
||||
|
||||
ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement )
|
||||
: elementCount_( 0 )
|
||||
{
|
||||
Init( bundleElement.Contents(), bundleElement.Size() );
|
||||
}
|
||||
|
||||
|
||||
void ReceivedBundle::Init( const char *bundle, osc_bundle_element_size_t size )
|
||||
{
|
||||
|
||||
if( !IsValidElementSizeValue(size) )
|
||||
throw MalformedBundleException( "invalid bundle size" );
|
||||
|
||||
if( size < 16 )
|
||||
throw MalformedBundleException( "packet too short for bundle" );
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
throw MalformedBundleException( "bundle size must be multiple of four" );
|
||||
|
||||
if( bundle[0] != '#'
|
||||
|| bundle[1] != 'b'
|
||||
|| bundle[2] != 'u'
|
||||
|| bundle[3] != 'n'
|
||||
|| bundle[4] != 'd'
|
||||
|| bundle[5] != 'l'
|
||||
|| bundle[6] != 'e'
|
||||
|| bundle[7] != '\0' )
|
||||
throw MalformedBundleException( "bad bundle address pattern" );
|
||||
|
||||
end_ = bundle + size;
|
||||
|
||||
timeTag_ = bundle + 8;
|
||||
|
||||
const char *p = timeTag_ + 8;
|
||||
|
||||
while( p < end_ ){
|
||||
if( p + osc::OSC_SIZEOF_INT32 > end_ )
|
||||
throw MalformedBundleException( "packet too short for elementSize" );
|
||||
|
||||
// treat element size as an unsigned int for the purposes of this calculation
|
||||
uint32 elementSize = ToUInt32( p );
|
||||
if( (elementSize & ((uint32)0x03)) != 0 )
|
||||
throw MalformedBundleException( "bundle element size must be multiple of four" );
|
||||
|
||||
p += osc::OSC_SIZEOF_INT32 + elementSize;
|
||||
if( p > end_ )
|
||||
throw MalformedBundleException( "packet too short for bundle element" );
|
||||
|
||||
++elementCount_;
|
||||
}
|
||||
|
||||
if( p != end_ )
|
||||
throw MalformedBundleException( "bundle contents " );
|
||||
}
|
||||
|
||||
|
||||
uint64 ReceivedBundle::TimeTag() const
|
||||
{
|
||||
return ToUInt64( timeTag_ );
|
||||
}
|
||||
|
||||
|
||||
} // namespace osc
|
||||
|
||||
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
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"
|
||||
#include "OscException.h"
|
||||
|
||||
|
||||
namespace osc{
|
||||
|
||||
|
||||
class MalformedPacketException : public Exception{
|
||||
public:
|
||||
MalformedPacketException( const char *w="malformed packet" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MalformedMessageException : public Exception{
|
||||
public:
|
||||
MalformedMessageException( const char *w="malformed message" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MalformedBundleException : public Exception{
|
||||
public:
|
||||
MalformedBundleException( const char *w="malformed bundle" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class WrongArgumentTypeException : public Exception{
|
||||
public:
|
||||
WrongArgumentTypeException( const char *w="wrong argument type" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class MissingArgumentException : public Exception{
|
||||
public:
|
||||
MissingArgumentException( const char *w="missing argument" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
class ExcessArgumentException : public Exception{
|
||||
public:
|
||||
ExcessArgumentException( const char *w="too many arguments" )
|
||||
: Exception( w ) {}
|
||||
};
|
||||
|
||||
|
||||
class ReceivedPacket{
|
||||
public:
|
||||
// Although the OSC spec is not entirely clear on this, we only support
|
||||
// packets up to 0x7FFFFFFC bytes long (the maximum 4-byte aligned value
|
||||
// representable by an int32). An exception will be raised if you pass a
|
||||
// larger value to the ReceivedPacket() constructor.
|
||||
|
||||
ReceivedPacket( const char *contents, osc_bundle_element_size_t size )
|
||||
: contents_( contents )
|
||||
, size_( ValidateSize(size) ) {}
|
||||
|
||||
ReceivedPacket( const char *contents, std::size_t size )
|
||||
: contents_( contents )
|
||||
, size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {}
|
||||
|
||||
#if !(defined(__x86_64__) || defined(_M_X64))
|
||||
ReceivedPacket( const char *contents, int size )
|
||||
: contents_( contents )
|
||||
, size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {}
|
||||
#endif
|
||||
|
||||
bool IsMessage() const { return !IsBundle(); }
|
||||
bool IsBundle() const;
|
||||
|
||||
osc_bundle_element_size_t Size() const { return size_; }
|
||||
const char *Contents() const { return contents_; }
|
||||
|
||||
private:
|
||||
const char *contents_;
|
||||
osc_bundle_element_size_t size_;
|
||||
|
||||
static osc_bundle_element_size_t ValidateSize( osc_bundle_element_size_t size )
|
||||
{
|
||||
// sanity check integer types declared in OscTypes.h
|
||||
// you'll need to fix OscTypes.h if any of these asserts fail
|
||||
assert( sizeof(osc::int32) == 4 );
|
||||
assert( sizeof(osc::uint32) == 4 );
|
||||
assert( sizeof(osc::int64) == 8 );
|
||||
assert( sizeof(osc::uint64) == 8 );
|
||||
|
||||
if( !IsValidElementSizeValue(size) )
|
||||
throw MalformedPacketException( "invalid packet size" );
|
||||
|
||||
if( size == 0 )
|
||||
throw MalformedPacketException( "zero length elements not permitted" );
|
||||
|
||||
if( !IsMultipleOf4(size) )
|
||||
throw MalformedPacketException( "element size must be multiple of four" );
|
||||
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ReceivedBundleElement{
|
||||
public:
|
||||
ReceivedBundleElement( const char *sizePtr )
|
||||
: sizePtr_( sizePtr ) {}
|
||||
|
||||
friend class ReceivedBundleElementIterator;
|
||||
|
||||
bool IsMessage() const { return !IsBundle(); }
|
||||
bool IsBundle() const;
|
||||
|
||||
osc_bundle_element_size_t Size() const;
|
||||
const char *Contents() const { return sizePtr_ + osc::OSC_SIZEOF_INT32; }
|
||||
|
||||
private:
|
||||
const char *sizePtr_;
|
||||
};
|
||||
|
||||
|
||||
class ReceivedBundleElementIterator{
|
||||
public:
|
||||
ReceivedBundleElementIterator( const char *sizePtr )
|
||||
: value_( sizePtr ) {}
|
||||
|
||||
ReceivedBundleElementIterator operator++()
|
||||
{
|
||||
Advance();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedBundleElementIterator operator++(int)
|
||||
{
|
||||
ReceivedBundleElementIterator old( *this );
|
||||
Advance();
|
||||
return old;
|
||||
}
|
||||
|
||||
const ReceivedBundleElement& operator*() const { return value_; }
|
||||
|
||||
const ReceivedBundleElement* operator->() const { return &value_; }
|
||||
|
||||
friend bool operator==(const ReceivedBundleElementIterator& lhs,
|
||||
const ReceivedBundleElementIterator& rhs );
|
||||
|
||||
private:
|
||||
ReceivedBundleElement value_;
|
||||
|
||||
void Advance() { value_.sizePtr_ = value_.Contents() + value_.Size(); }
|
||||
|
||||
bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const
|
||||
{
|
||||
return value_.sizePtr_ == rhs.value_.sizePtr_;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const ReceivedBundleElementIterator& lhs,
|
||||
const ReceivedBundleElementIterator& rhs )
|
||||
{
|
||||
return lhs.IsEqualTo( rhs );
|
||||
}
|
||||
|
||||
inline bool operator!=(const ReceivedBundleElementIterator& lhs,
|
||||
const ReceivedBundleElementIterator& rhs )
|
||||
{
|
||||
return !( lhs == rhs );
|
||||
}
|
||||
|
||||
|
||||
class ReceivedMessageArgument{
|
||||
public:
|
||||
ReceivedMessageArgument( const char *typeTagPtr, const char *argumentPtr )
|
||||
: typeTagPtr_( typeTagPtr )
|
||||
, argumentPtr_( argumentPtr ) {}
|
||||
|
||||
friend class ReceivedMessageArgumentIterator;
|
||||
|
||||
char TypeTag() const { return *typeTagPtr_; }
|
||||
|
||||
// the unchecked methods below don't check whether the argument actually
|
||||
// is of the specified type. they should only be used if you've already
|
||||
// checked the type tag or the associated IsType() method.
|
||||
|
||||
bool IsBool() const
|
||||
{ return *typeTagPtr_ == TRUE_TYPE_TAG || *typeTagPtr_ == FALSE_TYPE_TAG; }
|
||||
bool AsBool() const;
|
||||
bool AsBoolUnchecked() const;
|
||||
|
||||
bool IsNil() const { return *typeTagPtr_ == NIL_TYPE_TAG; }
|
||||
bool IsInfinitum() const { return *typeTagPtr_ == INFINITUM_TYPE_TAG; }
|
||||
|
||||
bool IsInt32() const { return *typeTagPtr_ == INT32_TYPE_TAG; }
|
||||
int32 AsInt32() const;
|
||||
int32 AsInt32Unchecked() const;
|
||||
|
||||
bool IsFloat() const { return *typeTagPtr_ == FLOAT_TYPE_TAG; }
|
||||
float AsFloat() const;
|
||||
float AsFloatUnchecked() const;
|
||||
|
||||
bool IsChar() const { return *typeTagPtr_ == CHAR_TYPE_TAG; }
|
||||
char AsChar() const;
|
||||
char AsCharUnchecked() const;
|
||||
|
||||
bool IsRgbaColor() const { return *typeTagPtr_ == RGBA_COLOR_TYPE_TAG; }
|
||||
uint32 AsRgbaColor() const;
|
||||
uint32 AsRgbaColorUnchecked() const;
|
||||
|
||||
bool IsMidiMessage() const { return *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG; }
|
||||
uint32 AsMidiMessage() const;
|
||||
uint32 AsMidiMessageUnchecked() const;
|
||||
|
||||
bool IsInt64() const { return *typeTagPtr_ == INT64_TYPE_TAG; }
|
||||
int64 AsInt64() const;
|
||||
int64 AsInt64Unchecked() const;
|
||||
|
||||
bool IsTimeTag() const { return *typeTagPtr_ == TIME_TAG_TYPE_TAG; }
|
||||
uint64 AsTimeTag() const;
|
||||
uint64 AsTimeTagUnchecked() const;
|
||||
|
||||
bool IsDouble() const { return *typeTagPtr_ == DOUBLE_TYPE_TAG; }
|
||||
double AsDouble() const;
|
||||
double AsDoubleUnchecked() const;
|
||||
|
||||
bool IsString() const { return *typeTagPtr_ == STRING_TYPE_TAG; }
|
||||
const char* AsString() const;
|
||||
const char* AsStringUnchecked() const { return argumentPtr_; }
|
||||
|
||||
bool IsSymbol() const { return *typeTagPtr_ == SYMBOL_TYPE_TAG; }
|
||||
const char* AsSymbol() const;
|
||||
const char* AsSymbolUnchecked() const { return argumentPtr_; }
|
||||
|
||||
bool IsBlob() const { return *typeTagPtr_ == BLOB_TYPE_TAG; }
|
||||
void AsBlob( const void*& data, osc_bundle_element_size_t& size ) const;
|
||||
void AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const;
|
||||
|
||||
bool IsArrayBegin() const { return *typeTagPtr_ == ARRAY_BEGIN_TYPE_TAG; }
|
||||
bool IsArrayEnd() const { return *typeTagPtr_ == ARRAY_END_TYPE_TAG; }
|
||||
// Calculate the number of top-level items in the array. Nested arrays count as one item.
|
||||
// Only valid at array start. Will throw an exception if IsArrayStart() == false.
|
||||
std::size_t ComputeArrayItemCount() const;
|
||||
|
||||
private:
|
||||
const char *typeTagPtr_;
|
||||
const char *argumentPtr_;
|
||||
};
|
||||
|
||||
|
||||
class ReceivedMessageArgumentIterator{
|
||||
public:
|
||||
ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments )
|
||||
: value_( typeTags, arguments ) {}
|
||||
|
||||
ReceivedMessageArgumentIterator operator++()
|
||||
{
|
||||
Advance();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentIterator operator++(int)
|
||||
{
|
||||
ReceivedMessageArgumentIterator old( *this );
|
||||
Advance();
|
||||
return old;
|
||||
}
|
||||
|
||||
const ReceivedMessageArgument& operator*() const { return value_; }
|
||||
|
||||
const ReceivedMessageArgument* operator->() const { return &value_; }
|
||||
|
||||
friend bool operator==(const ReceivedMessageArgumentIterator& lhs,
|
||||
const ReceivedMessageArgumentIterator& rhs );
|
||||
|
||||
private:
|
||||
ReceivedMessageArgument value_;
|
||||
|
||||
void Advance();
|
||||
|
||||
bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const
|
||||
{
|
||||
return value_.typeTagPtr_ == rhs.value_.typeTagPtr_;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const ReceivedMessageArgumentIterator& lhs,
|
||||
const ReceivedMessageArgumentIterator& rhs )
|
||||
{
|
||||
return lhs.IsEqualTo( rhs );
|
||||
}
|
||||
|
||||
inline bool operator!=(const ReceivedMessageArgumentIterator& lhs,
|
||||
const ReceivedMessageArgumentIterator& rhs )
|
||||
{
|
||||
return !( lhs == rhs );
|
||||
}
|
||||
|
||||
|
||||
class ReceivedMessageArgumentStream{
|
||||
friend class ReceivedMessage;
|
||||
ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin,
|
||||
const ReceivedMessageArgumentIterator& end )
|
||||
: p_( begin )
|
||||
, end_( end ) {}
|
||||
|
||||
ReceivedMessageArgumentIterator p_, end_;
|
||||
|
||||
public:
|
||||
|
||||
// end of stream
|
||||
bool Eos() const { return p_ == end_; }
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( bool& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsBool();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// not sure if it would be useful to stream Nil and Infinitum
|
||||
// for now it's not possible
|
||||
// same goes for array boundaries
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( int32& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsInt32();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( float& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsFloat();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( char& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsChar();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs.value = (*p_++).AsRgbaColor();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs.value = (*p_++).AsMidiMessage();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( int64& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsInt64();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( TimeTag& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs.value = (*p_++).AsTimeTag();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( double& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsDouble();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( Blob& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
(*p_++).AsBlob( rhs.data, rhs.size );
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( const char*& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs = (*p_++).AsString();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( Symbol& rhs )
|
||||
{
|
||||
if( Eos() )
|
||||
throw MissingArgumentException();
|
||||
|
||||
rhs.value = (*p_++).AsSymbol();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs )
|
||||
{
|
||||
(void) rhs; // suppress unused parameter warning
|
||||
|
||||
if( !Eos() )
|
||||
throw ExcessArgumentException();
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ReceivedMessage{
|
||||
void Init( const char *bundle, osc_bundle_element_size_t size );
|
||||
public:
|
||||
explicit ReceivedMessage( const ReceivedPacket& packet );
|
||||
explicit ReceivedMessage( const ReceivedBundleElement& bundleElement );
|
||||
|
||||
const char *AddressPattern() const { return addressPattern_; }
|
||||
|
||||
// Support for non-standard SuperCollider integer address patterns:
|
||||
bool AddressPatternIsUInt32() const;
|
||||
uint32 AddressPatternAsUInt32() const;
|
||||
|
||||
uint32 ArgumentCount() const { return static_cast<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_;
|
||||
};
|
||||
|
||||
|
||||
class ReceivedBundle{
|
||||
void Init( const char *message, osc_bundle_element_size_t size );
|
||||
public:
|
||||
explicit ReceivedBundle( const ReceivedPacket& packet );
|
||||
explicit ReceivedBundle( const ReceivedBundleElement& bundleElement );
|
||||
|
||||
uint64 TimeTag() const;
|
||||
|
||||
uint32 ElementCount() const { return elementCount_; }
|
||||
|
||||
typedef ReceivedBundleElementIterator const_iterator;
|
||||
|
||||
ReceivedBundleElementIterator ElementsBegin() const
|
||||
{
|
||||
return ReceivedBundleElementIterator( timeTag_ + 8 );
|
||||
}
|
||||
|
||||
ReceivedBundleElementIterator ElementsEnd() const
|
||||
{
|
||||
return ReceivedBundleElementIterator( end_ );
|
||||
}
|
||||
|
||||
private:
|
||||
const char *timeTag_;
|
||||
const char *end_;
|
||||
uint32 elementCount_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H */
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
} // namespace osc
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
} // namespace osc
|
||||
|
||||
|
||||
#endif /* INCLUDED_OSCPACK_OSCTYPES_H */
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
/* Public Dependencies
|
||||
*****************************************************************************/
|
||||
|
||||
#include "Networking.h"
|
||||
|
||||
|
||||
/* Public Includes
|
||||
*****************************************************************************/
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscDispatcher.h"
|
||||
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FOscModule"
|
||||
|
||||
|
||||
class FOscModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule( ) override
|
||||
{
|
||||
if(!FModuleManager::Get().LoadModule(TEXT("Networking")).IsValid())
|
||||
{
|
||||
UE_LOG(LogOSC, Error, TEXT("Required module Networking failed to load"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto settings = GetMutableDefault<UOscSettings>();
|
||||
|
||||
_dispatcher = UOscDispatcher::Get();
|
||||
_dispatcher->Listen(settings->ReceivePort);
|
||||
settings->UpdateSendAddresses();
|
||||
|
||||
// register settings
|
||||
ISettingsModule* settingsModule = ISettingsModule::Get();
|
||||
if(settingsModule)
|
||||
{
|
||||
FSettingsSectionDelegates settingsDelegates;
|
||||
settingsDelegates.ModifiedDelegate = FOnSettingsSectionModified::CreateRaw(this, &FOscModule::HandleSettingsSaved);
|
||||
|
||||
settingsModule->RegisterSettings("Project", "Plugins", "OSC",
|
||||
LOCTEXT("OscSettingsName", "OSC"),
|
||||
LOCTEXT("OscSettingsDescription", "Configure the OSC plug-in."),
|
||||
settings,
|
||||
settingsDelegates
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Warning, TEXT("Settings changed registration failed"));
|
||||
}
|
||||
UE_LOG(LogOSC, Display, TEXT("Startup succeed"));
|
||||
}
|
||||
|
||||
virtual void ShutdownModule( ) override
|
||||
{
|
||||
UE_LOG(LogOSC, Display, TEXT("Shutdown"));
|
||||
if(_dispatcher.IsValid())
|
||||
{
|
||||
_dispatcher->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleSettingsSaved()
|
||||
{
|
||||
UE_LOG(LogOSC, Display, TEXT("Update settings"));
|
||||
UOscSettings* settings = GetMutableDefault<UOscSettings>();
|
||||
if(_dispatcher.IsValid())
|
||||
{
|
||||
_dispatcher->Listen(settings->ReceivePort);
|
||||
}
|
||||
settings->UpdateSendAddresses();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TWeakObjectPtr<UOscDispatcher> _dispatcher;
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_MODULE(FOscModule, OSC);
|
||||
DEFINE_LOG_CATEGORY(LogOSC);
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Osc.h"
|
||||
|
||||
#include "Core.h"
|
||||
#include "ModuleManager.h"
|
||||
#include "Settings.h"
|
||||
#include "Sockets.h"
|
||||
#include "SocketSubsystem.h"
|
||||
#include "TaskGraphInterfaces.h"
|
||||
|
||||
#include "Runtime/Engine/Classes/Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Runtime/Networking/Public/Networking.h"
|
||||
|
||||
#include "OscSettings.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOSC, Log, All);
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
|
||||
|
||||
UOscSettings::UOscSettings( const class FPostConstructInitializeProperties& PCIP )
|
||||
: Super(PCIP),
|
||||
ReceivePort(8000),
|
||||
_sendSocket(FUdpSocketBuilder(TEXT("OscSender")).Build())
|
||||
{
|
||||
SendTargets.Add(TEXT("127.0.0.1:8000"));
|
||||
}
|
||||
|
||||
void UOscSettings::UpdateSendAddresses()
|
||||
{
|
||||
FString addressStr, portStr;
|
||||
for(int32 i=0, n=SendTargets.Num(); i!=n; ++i)
|
||||
{
|
||||
if(i >= _sendAddresses.Num())
|
||||
{
|
||||
_sendAddresses.Emplace(FString(), ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr());
|
||||
}
|
||||
|
||||
if(_sendAddresses[i].first != SendTargets[i])
|
||||
{
|
||||
int32 sep = 0;
|
||||
bool isValid = false;
|
||||
if(SendTargets[i].FindChar(TEXT(':'), sep))
|
||||
{
|
||||
const FString ip = SendTargets[i].Left(sep).Trim();
|
||||
_sendAddresses[i].second->SetIp(&ip.GetCharArray()[0], isValid);
|
||||
if(isValid)
|
||||
{
|
||||
const int32 port = FCString::Atoi(&SendTargets[i].GetCharArray()[sep+1]);
|
||||
_sendAddresses[i].second->SetPort(port);
|
||||
_sendAddresses[i].first = SendTargets[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOscSettings::Send(const uint8 *buffer, int32 length, int32 targetIndex)
|
||||
{
|
||||
if(targetIndex < _sendAddresses.Num())
|
||||
{
|
||||
int32 bytesSent = 0;
|
||||
while(length > 0)
|
||||
{
|
||||
_sendSocket->SendTo(buffer, length, bytesSent, *_sendAddresses[targetIndex].second);
|
||||
length -= bytesSent;
|
||||
buffer += bytesSent;
|
||||
}
|
||||
UE_LOG(LogOSC, Verbose, TEXT("OSC sent"));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Warning, TEXT("Cannot send OSC: invalid targetIndex %d"), targetIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include "OscSettings.generated.h"
|
||||
|
||||
|
||||
UCLASS(config=Engine)
|
||||
class UOscSettings : public UObject
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(Config, EditAnywhere, Category=Receive)
|
||||
int32 ReceivePort;
|
||||
|
||||
UPROPERTY(Config, EditAnywhere, Category=Send)
|
||||
TArray<FString> SendTargets;
|
||||
|
||||
public:
|
||||
void UpdateSendAddresses();
|
||||
|
||||
void Send(const uint8 *buffer, int32 length, int32 targetIndex);
|
||||
|
||||
private:
|
||||
TSharedRef<FSocket> _sendSocket;
|
||||
TArray<std::pair<FString, TSharedRef<FInternetAddr>>> _sendAddresses;
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
|
||||
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscDispatcher.h"
|
||||
#include "OscReceiverInterface.h"
|
||||
|
||||
#include "oscpack/osc/OscReceivedElements.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
UOscDispatcher::UOscDispatcher(const class FPostConstructInitializeProperties& PCIP)
|
||||
: Super(PCIP),
|
||||
_currentPort(0),
|
||||
_socket(nullptr),
|
||||
_socketReceiver(nullptr),
|
||||
_pendingMessages(64)
|
||||
{
|
||||
}
|
||||
|
||||
UOscDispatcher * UOscDispatcher::Get()
|
||||
{
|
||||
return UOscDispatcher::StaticClass()->GetDefaultObject<UOscDispatcher>();
|
||||
}
|
||||
|
||||
void UOscDispatcher::Listen(uint32_t port)
|
||||
{
|
||||
if(_currentPort != port)
|
||||
{
|
||||
delete _socket;
|
||||
delete _socketReceiver;
|
||||
|
||||
_socket = FUdpSocketBuilder(TEXT("OscListener")).BoundToAddress(FIPv4Address::Any).BoundToPort(port).Build();
|
||||
if(_socket)
|
||||
{
|
||||
_socketReceiver = new FUdpSocketReceiver(_socket, FTimespan(0, 0, 0, 10), TEXT("OSCListener"));
|
||||
_socketReceiver->OnDataReceived().BindUObject(this, &UOscDispatcher::Callback);
|
||||
|
||||
_currentPort = port;
|
||||
UE_LOG(LogOSC, Display, TEXT("Listen to port %d"), port);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOSC, Warning, TEXT("Cannot listen port %d"), port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOscDispatcher::Stop()
|
||||
{
|
||||
UE_LOG(LogOSC, Display, TEXT("Stop listening"));
|
||||
if(_socket)
|
||||
{
|
||||
_socket->Close();
|
||||
}
|
||||
if(_socketReceiver)
|
||||
{
|
||||
_socketReceiver->OnDataReceived().Unbind();
|
||||
_socketReceiver->Stop();
|
||||
|
||||
// wait longer than the socket read wait time, or it will crash.
|
||||
FPlatformProcess::Sleep(0.1f);
|
||||
}
|
||||
|
||||
delete _socket;
|
||||
delete _socketReceiver;
|
||||
_socket = nullptr;
|
||||
_socketReceiver = nullptr;
|
||||
|
||||
_currentPort = 0;
|
||||
}
|
||||
|
||||
void UOscDispatcher::RegisterReceiver(IOscReceiverInterface * receiver)
|
||||
{
|
||||
_receivers.AddUnique(receiver);
|
||||
}
|
||||
|
||||
void UOscDispatcher::UnregisterReceiver(IOscReceiverInterface * receiver)
|
||||
{
|
||||
_receivers.Remove(receiver);
|
||||
}
|
||||
|
||||
static void SendMessage(TCircularQueue<std::pair<FName, FOscDataStruct>> & _pendingMessages, const osc::ReceivedMessage & message)
|
||||
{
|
||||
const FName address(message.AddressPattern());
|
||||
|
||||
FOscDataStruct data;
|
||||
|
||||
const auto argBegin = message.ArgumentsBegin();
|
||||
const auto argEnd = message.ArgumentsEnd();
|
||||
for(auto it = argBegin; it != argEnd; ++it)
|
||||
{
|
||||
FOscDataElemStruct elem;
|
||||
if(it->IsFloat())
|
||||
{
|
||||
elem.SetFloat(it->AsFloatUnchecked());
|
||||
}
|
||||
else if(it->IsDouble())
|
||||
{
|
||||
elem.SetFloat(it->AsDoubleUnchecked());
|
||||
}
|
||||
else if(it->IsInt32())
|
||||
{
|
||||
elem.SetInt(it->AsInt32Unchecked());
|
||||
}
|
||||
else if(it->IsInt64())
|
||||
{
|
||||
elem.SetInt(it->AsInt64Unchecked());
|
||||
}
|
||||
else if(it->IsBool())
|
||||
{
|
||||
elem.SetBool(it->AsBoolUnchecked());
|
||||
}
|
||||
else if(it->IsString())
|
||||
{
|
||||
elem.SetString(FName(it->AsStringUnchecked()));
|
||||
}
|
||||
data.Queue.Add(elem);
|
||||
}
|
||||
|
||||
// save it in pending messages
|
||||
_pendingMessages.Enqueue(std::make_pair(address, data));
|
||||
}
|
||||
|
||||
static void SendBundle(TCircularQueue<std::pair<FName, FOscDataStruct>> & _pendingMessages, const osc::ReceivedBundle & bundle)
|
||||
{
|
||||
const auto begin = bundle.ElementsBegin();
|
||||
const auto end = bundle.ElementsEnd();
|
||||
for(auto it = begin; it != end; ++it)
|
||||
{
|
||||
if(it->IsBundle())
|
||||
{
|
||||
SendBundle(_pendingMessages, osc::ReceivedBundle(*it));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessage(_pendingMessages, osc::ReceivedMessage(*it));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOscDispatcher::Callback(const FArrayReaderPtr& data, const FIPv4Endpoint&)
|
||||
{
|
||||
UE_LOG(LogOSC, Verbose, TEXT("OSC Received"));
|
||||
const osc::ReceivedPacket packet((const char *)data->GetData(), data->Num());
|
||||
if(packet.IsBundle())
|
||||
{
|
||||
SendBundle(_pendingMessages, osc::ReceivedBundle(packet));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessage(_pendingMessages, osc::ReceivedMessage(packet));
|
||||
}
|
||||
|
||||
if(!_pendingMessages.IsEmpty())
|
||||
{
|
||||
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(
|
||||
FSimpleDelegateGraphTask::FDelegate::CreateUObject(this, &UOscDispatcher::CallbackMainThread),
|
||||
TEXT("OscDispatcherProcessMessages"),
|
||||
nullptr,
|
||||
ENamedThreads::GameThread);
|
||||
}
|
||||
}
|
||||
|
||||
void UOscDispatcher::CallbackMainThread()
|
||||
{
|
||||
std::pair<FName, FOscDataStruct> message;
|
||||
while(_pendingMessages.Dequeue(message))
|
||||
{
|
||||
for(auto receiver : _receivers)
|
||||
{
|
||||
receiver->SendEvent(message.first, message.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOscDispatcher::BeginDestroy()
|
||||
{
|
||||
Stop();
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataStruct.h"
|
||||
#include "OscDispatcher.generated.h"
|
||||
|
||||
class IOscReceiverInterface;
|
||||
class FSocket;
|
||||
class FUdpSocketReceiver;
|
||||
|
||||
|
||||
/// Singleton that dispatches the OSC messages to listeners.
|
||||
UCLASS()
|
||||
class UOscDispatcher : public UObject
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
friend class OscModule;
|
||||
|
||||
void Listen(uint32_t port);
|
||||
void Stop();
|
||||
|
||||
public:
|
||||
|
||||
/// Get the unique instance
|
||||
///
|
||||
/// The instance is the UObject default instance.
|
||||
static UOscDispatcher * Get();
|
||||
|
||||
/// Add the receiver in the listeners list
|
||||
void RegisterReceiver(IOscReceiverInterface * receiver);
|
||||
|
||||
/// Remove the receiver in the listeners list
|
||||
void UnregisterReceiver(IOscReceiverInterface * receiver);
|
||||
|
||||
private:
|
||||
void Callback(const FArrayReaderPtr& data, const FIPv4Endpoint&);
|
||||
|
||||
void CallbackMainThread();
|
||||
|
||||
void BeginDestroy() override;
|
||||
|
||||
private:
|
||||
TArray<IOscReceiverInterface *> _receivers;
|
||||
uint32_t _currentPort;
|
||||
FSocket * _socket;
|
||||
FUdpSocketReceiver * _socketReceiver;
|
||||
TCircularQueue<std::pair<FName, FOscDataStruct>> _pendingMessages;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscReceiverComponent.h"
|
||||
#include "OscDispatcher.h"
|
||||
|
||||
|
||||
UOscReceiverComponent::UOscReceiverComponent(const class FPostConstructInitializeProperties& PCIP)
|
||||
: Super(PCIP)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UOscReceiverComponent::OnRegister()
|
||||
{
|
||||
Super::OnRegister();
|
||||
|
||||
auto instance = UOscDispatcher::Get();
|
||||
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
||||
{
|
||||
instance->RegisterReceiver(this);
|
||||
}
|
||||
}
|
||||
|
||||
void UOscReceiverComponent::OnUnregister()
|
||||
{
|
||||
auto instance = UOscDispatcher::Get();
|
||||
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
||||
{
|
||||
instance->UnregisterReceiver(this);
|
||||
}
|
||||
|
||||
Super::OnUnregister();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "OscReceiverInterface.h"
|
||||
#include "OscDataStruct.h"
|
||||
#include "OscReceiverComponent.generated.h"
|
||||
|
||||
|
||||
// declare the OnOscReceived event type
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComponentOscReceivedSignature, const FName &, Address, const FOscDataStruct &, Data);
|
||||
|
||||
|
||||
UCLASS(meta = (BlueprintSpawnableComponent))
|
||||
class UOscReceiverComponent : public UActorComponent, public IOscReceiverInterface
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category=OSC)
|
||||
FString AddressFilter;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category=OSC)
|
||||
FComponentOscReceivedSignature OnOscReceived;
|
||||
|
||||
protected:
|
||||
virtual void OnRegister() override;
|
||||
|
||||
virtual void OnUnregister() override;
|
||||
|
||||
virtual const FString & GetAddressFilter() override
|
||||
{
|
||||
return AddressFilter;
|
||||
}
|
||||
|
||||
virtual void SendEvent(const FName & Address, const FOscDataStruct & Data) override
|
||||
{
|
||||
OnOscReceived.Broadcast(Address, Data);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "OscPrivatePCH.h"
|
||||
#include "OscReceiverInterface.h"
|
||||
|
||||
|
||||
UOscReceiverInterface::UOscReceiverInterface(const class FPostConstructInitializeProperties& PCIP)
|
||||
: Super(PCIP)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const FString & IOscReceiverInterface::GetAddressFilter()
|
||||
{
|
||||
static FString defaultValue;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void IOscReceiverInterface::SendEvent(const FName & Address, const FOscDataStruct & Data)
|
||||
{ }
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "OscDataStruct.h"
|
||||
#include "OscReceiverInterface.generated.h"
|
||||
|
||||
|
||||
UINTERFACE(MinimalAPI)
|
||||
class UOscReceiverInterface : public UInterface
|
||||
{
|
||||
GENERATED_UINTERFACE_BODY()
|
||||
};
|
||||
|
||||
class IOscReceiverInterface
|
||||
{
|
||||
GENERATED_IINTERFACE_BODY()
|
||||
|
||||
virtual const FString & GetAddressFilter();
|
||||
virtual void SendEvent(const FName & Address, const FOscDataStruct & Data);
|
||||
};
|
||||
@@ -2,3 +2,25 @@ UE4-OSC
|
||||
=======
|
||||
|
||||
OSC plugin for Unreal Engine 4 Blueprints
|
||||
|
||||
|
||||
Integration
|
||||
===========
|
||||
|
||||
Put the "OSC" folder in a "Plugins" folder in the project root.
|
||||
e.g. "/MyProject/Plugins/OSC"
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
- Receiver:
|
||||
Set the listening port in the plugin settings.
|
||||
Add a OscReceiverComponent to your Blueprint class.
|
||||
Bind the OnOscReceived custom event.
|
||||
Use the PopFloat/Int/Bool/String functions to retrieve the OSC message parameters.
|
||||
|
||||
- Sender:
|
||||
Set the "send to" addresses in the plugin settings.
|
||||
Use the PushFloat/Int/Bool/String functions to build the OSC message parameters.
|
||||
Use the SendOsc function to send the OSC message
|
||||
|
||||
Reference in New Issue
Block a user