initial commit

This commit is contained in:
monsieurgustav
2014-10-05 19:45:30 +02:00
parent c9aedbc6d4
commit 1d352e7216
27 changed files with 3671 additions and 0 deletions
+25
View File
@@ -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

+39
View File
@@ -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 */
+11
View File
@@ -0,0 +1,11 @@
#pragma once
/* Public Dependencies
*****************************************************************************/
#include "Networking.h"
/* Public Includes
*****************************************************************************/
+77
View File
@@ -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
+20
View File
@@ -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);
+58
View File
@@ -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);
}
}
+30
View File
@@ -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);
};
+22
View File
@@ -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