549 lines
16 KiB
C++
Executable File
549 lines
16 KiB
C++
Executable File
/*
|
|
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 */
|