1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Added support for colour values to the OSC classes

This commit is contained in:
jules 2018-07-09 14:02:58 +01:00
parent e59bde5ac6
commit 49df6e2696
8 changed files with 154 additions and 82 deletions

View file

@ -27,25 +27,11 @@
namespace juce
{
OSCArgument::OSCArgument (int32 value) noexcept
: type (OSCTypes::int32), intValue (value)
{
}
OSCArgument::OSCArgument (float value) noexcept
: type (OSCTypes::float32), floatValue (value)
{
}
OSCArgument::OSCArgument (const String& value) noexcept
: type (OSCTypes::string), stringValue (value)
{
}
OSCArgument::OSCArgument (const MemoryBlock& b)
: type (OSCTypes::blob), blob (b)
{
}
OSCArgument::OSCArgument (int32 v) : type (OSCTypes::int32), intValue (v) {}
OSCArgument::OSCArgument (float v) : type (OSCTypes::float32), floatValue (v) {}
OSCArgument::OSCArgument (const String& s) : type (OSCTypes::string), stringValue (s) {}
OSCArgument::OSCArgument (MemoryBlock b) : type (OSCTypes::blob), blob (static_cast<MemoryBlock&&> (b)) {}
OSCArgument::OSCArgument (OSCColour c) : type (OSCTypes::colour), intValue ((int32) c.toInt32()) {}
//==============================================================================
String OSCArgument::getString() const noexcept
@ -83,6 +69,14 @@ const MemoryBlock& OSCArgument::getBlob() const noexcept
return blob;
}
OSCColour OSCArgument::getColour() const noexcept
{
if (isColour())
return OSCColour::fromInt32 ((uint32) intValue);
jassertfalse; // you must check the type of an argument before attempting to get its value!
return { 0, 0, 0, 0 };
}
//==============================================================================
//==============================================================================
@ -120,11 +114,11 @@ public:
OSCArgument arg (value);
expect (arg.getType() == OSCTypes::int32);
expect (arg.isInt32());
expect (! arg.isFloat32());
expect (! arg.isString());
expect (! arg.isBlob());
expect (! arg.isColour());
expect (arg.getInt32() == value);
}
@ -136,15 +130,13 @@ public:
OSCArgument arg (value);
expect (arg.getType() == OSCTypes::float32);
expect (! arg.isInt32());
expect (arg.isFloat32());
expect (! arg.isString());
expect (! arg.isBlob());
expect (! arg.isColour());
expect (arg.getFloat32() == value);
}
beginTest ("String");
@ -153,11 +145,11 @@ public:
OSCArgument arg (value);
expect (arg.getType() == OSCTypes::string);
expect (! arg.isInt32());
expect (! arg.isFloat32());
expect (arg.isString());
expect (! arg.isBlob());
expect (! arg.isColour());
expect (arg.getString() == value);
}
@ -167,32 +159,54 @@ public:
OSCArgument arg ("Hello, World!");
expect (arg.getType() == OSCTypes::string);
expect (! arg.isInt32());
expect (! arg.isFloat32());
expect (arg.isString());
expect (! arg.isBlob());
expect (! arg.isColour());
expect (arg.getString() == String ("Hello, World!"));
expect (arg.getString() == "Hello, World!");
}
beginTest ("Blob");
{
const size_t numBytes = 412;
MemoryBlock blob = getMemoryBlockWithRandomData (numBytes);
auto blob = getMemoryBlockWithRandomData (413);
OSCArgument arg (blob);
expect (arg.getType() == OSCTypes::blob);
expect (! arg.isInt32());
expect (! arg.isFloat32());
expect (! arg.isString());
expect (arg.isBlob());
expect (! arg.isColour());
expect (arg.getBlob() == blob);
}
beginTest ("Colour");
{
Random rng = getRandom();
for (int i = 100; --i >= 0;)
{
OSCColour col = { (uint8) rng.nextInt (256),
(uint8) rng.nextInt (256),
(uint8) rng.nextInt (256),
(uint8) rng.nextInt (256) };
OSCArgument arg (col);
expect (arg.getType() == OSCTypes::colour);
expect (! arg.isInt32());
expect (! arg.isFloat32());
expect (! arg.isString());
expect (! arg.isBlob());
expect (arg.isColour());
expect (arg.getColour().toInt32() == col.toInt32());
}
}
beginTest ("Copy, move and assignment");
{
{

View file

@ -42,13 +42,13 @@ class JUCE_API OSCArgument
{
public:
/** Constructs an OSCArgument with type int32 and a given value. */
OSCArgument (int32 value) noexcept;
OSCArgument (int32 value);
/** Constructs an OSCArgument with type float32 and a given value. */
OSCArgument (float value) noexcept;
OSCArgument (float value);
/** Constructs an OSCArgument with type string and a given value */
OSCArgument (const String& value) noexcept;
OSCArgument (const String& value);
/** Constructs an OSCArgument with type blob and copies dataSize bytes
from the memory pointed to by data into the blob.
@ -56,7 +56,10 @@ public:
The data owned by the blob will be released when the OSCArgument object
gets destructed.
*/
OSCArgument (const MemoryBlock& blob);
OSCArgument (MemoryBlock blob);
/** Constructs an OSCArgument with type colour and a given colour value */
OSCArgument (OSCColour colour);
/** Returns the type of the OSCArgument as an OSCType.
OSCType is a char type, and its value will be the OSC type tag of the type.
@ -75,28 +78,35 @@ public:
/** Returns whether the type of the OSCArgument is blob. */
bool isBlob() const noexcept { return type == OSCTypes::blob; }
/** Returns whether the type of the OSCArgument is blob. */
bool isColour() const noexcept { return type == OSCTypes::colour; }
/** Returns the value of the OSCArgument as an int32.
If the type of the OSCArgument is not int32, the behaviour is undefined.
*/
*/
int32 getInt32() const noexcept;
/** Returns the value of the OSCArgument as a float32.
If the type of the OSCArgument is not float32, the behaviour is undefined.
*/
*/
float getFloat32() const noexcept;
/** Returns the value of the OSCArgument as a string.
If the type of the OSCArgument is not string, the behaviour is undefined.
*/
*/
String getString() const noexcept;
/** Returns the binary data contained in the blob and owned by the OSCArgument,
as a reference to a JUCE MemoryBlock object.
If the type of the OSCArgument is not blob, the behaviour is undefined.
*/
*/
const MemoryBlock& getBlob() const noexcept;
/** Returns the value of the OSCArgument as an OSCColour.
If the type of the OSCArgument is not a colour, the behaviour is undefined.
*/
OSCColour getColour() const noexcept;
private:
//==============================================================================

View file

@ -82,7 +82,8 @@ void OSCMessage::clear()
void OSCMessage::addInt32 (int32 value) { arguments.add (OSCArgument (value)); }
void OSCMessage::addFloat32 (float value) { arguments.add (OSCArgument (value)); }
void OSCMessage::addString (const String& value) { arguments.add (OSCArgument (value)); }
void OSCMessage::addBlob (const MemoryBlock& blob) { arguments.add (OSCArgument (blob)); }
void OSCMessage::addBlob (MemoryBlock blob) { arguments.add (OSCArgument (static_cast<MemoryBlock&&> (blob))); }
void OSCMessage::addColour (OSCColour colour) { arguments.add (OSCArgument (colour)); }
void OSCMessage::addArgument (OSCArgument arg) { arguments.add (arg); }
//==============================================================================
@ -102,11 +103,12 @@ public:
expectEquals (msg.size(), 0);
expect (msg.getAddressPattern().toString() == "/test/param0");
const int numTestArgs = 4;
const int numTestArgs = 5;
const int testInt = 42;
const float testFloat = 3.14159f;
const String testString = "Hello, World!";
const OSCColour testColour = { 10, 20, 150, 200 };
const uint8 testBlobData[5] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
@ -115,6 +117,7 @@ public:
msg.addFloat32 (testFloat);
msg.addString (testString);
msg.addBlob (testBlob);
msg.addColour (testColour);
expectEquals (msg.size(), numTestArgs);
@ -122,20 +125,23 @@ public:
expectEquals (msg[1].getType(), OSCTypes::float32);
expectEquals (msg[2].getType(), OSCTypes::string);
expectEquals (msg[3].getType(), OSCTypes::blob);
expectEquals (msg[4].getType(), OSCTypes::colour);
expect (msg[0].isInt32());
expect (msg[1].isFloat32());
expect (msg[2].isString());
expect (msg[3].isBlob());
expect (msg[4].isColour());
expectEquals (msg[0].getInt32(), testInt);
expectEquals (msg[1].getFloat32(), testFloat);
expectEquals (msg[2].getString(), testString);
expect (msg[3].getBlob() == testBlob);
expect (msg[4].getColour().toInt32() == testColour.toInt32());
expect (msg.begin() + numTestArgs == msg.end());
OSCArgument* arg = msg.begin();
auto arg = msg.begin();
expect (arg->isInt32());
expectEquals (arg->getInt32(), testInt);
++arg;
@ -146,7 +152,10 @@ public:
expectEquals (arg->getString(), testString);
++arg;
expect (arg->isBlob());
expect(arg->getBlob() == testBlob);
expect (arg->getBlob() == testBlob);
++arg;
expect (arg->isColour());
expect (arg->getColour().toInt32() == testColour.toInt32());
++arg;
expect (arg == msg.end());
}

View file

@ -110,17 +110,17 @@ public:
void clear();
//==============================================================================
/** Creates a new OSCArgument of type int32 with a given value
/** Creates a new OSCArgument of type int32 with the given value,
and adds it to the OSCMessage object.
*/
void addInt32 (int32 value);
/** Creates a new OSCArgument of type float32 with a given value
/** Creates a new OSCArgument of type float32 with the given value,
and adds it to the OSCMessage object.
*/
void addFloat32 (float value);
/** Creates a new OSCArgument of type string with a given value
/** Creates a new OSCArgument of type string with the given value,
and adds it to the OSCMessage object.
*/
void addString (const String& value);
@ -130,7 +130,12 @@ public:
Note: if the argument passed is an lvalue, this may copy the binary data.
*/
void addBlob (const MemoryBlock& blob);
void addBlob (MemoryBlock blob);
/** Creates a new OSCArgument of type colour with the given value,
and adds it to the OSCMessage object.
*/
void addColour (OSCColour colour);
/** Adds the OSCArgument argument to the OSCMessage object.

View file

@ -74,32 +74,25 @@ namespace
//==============================================================================
int32 readInt32()
{
if (input.getNumBytesRemaining() < 4)
throw OSCFormatError ("OSC input stream exhausted while reading int32");
checkBytesAvailable (4, "OSC input stream exhausted while reading int32");
return input.readIntBigEndian();
}
uint64 readUint64()
{
if (input.getNumBytesRemaining() < 8)
throw OSCFormatError ("OSC input stream exhausted while reading uint64");
checkBytesAvailable (8, "OSC input stream exhausted while reading uint64");
return (uint64) input.readInt64BigEndian();
}
float readFloat32()
{
if (input.getNumBytesRemaining() < 4)
throw OSCFormatError ("OSC input stream exhausted while reading float");
checkBytesAvailable (4, "OSC input stream exhausted while reading float");
return input.readFloatBigEndian();
}
String readString()
{
if (input.getNumBytesRemaining() < 4)
throw OSCFormatError ("OSC input stream exhausted while reading string");
checkBytesAvailable (4, "OSC input stream exhausted while reading string");
auto posBegin = (size_t) getPosition();
auto s = input.readString();
@ -116,27 +109,27 @@ namespace
MemoryBlock readBlob()
{
if (input.getNumBytesRemaining() < 4)
throw OSCFormatError ("OSC input stream exhausted while reading blob");
checkBytesAvailable (4, "OSC input stream exhausted while reading blob");
auto blobDataSize = (size_t) input.readIntBigEndian();
if ((size_t) input.getNumBytesRemaining() < (blobDataSize + 3) % 4)
throw OSCFormatError ("OSC input stream exhausted before reaching end of blob");
auto blobDataSize = input.readIntBigEndian();
checkBytesAvailable ((blobDataSize + 3) % 4, "OSC input stream exhausted before reaching end of blob");
MemoryBlock blob;
auto bytesRead = input.readIntoMemoryBlock (blob, (ssize_t) blobDataSize);
readPaddingZeros (bytesRead);
return blob;
}
OSCColour readColour()
{
checkBytesAvailable (4, "OSC input stream exhausted while reading colour");
return OSCColour::fromInt32 ((uint32) input.readIntBigEndian());
}
OSCTimeTag readTimeTag()
{
if (input.getNumBytesRemaining() < 8)
throw OSCFormatError ("OSC input stream exhausted while reading time tag");
checkBytesAvailable (8, "OSC input stream exhausted while reading time tag");
return OSCTimeTag (uint64 (input.readInt64BigEndian()));
}
@ -155,8 +148,7 @@ namespace
{
OSCTypeList typeList;
if (input.getNumBytesRemaining() < 4)
throw OSCFormatError ("OSC input stream exhausted while reading type tag string");
checkBytesAvailable (4, "OSC input stream exhausted while reading type tag string");
if (input.readByte() != ',')
throw OSCFormatError ("OSC input stream format error: expected type tag string");
@ -192,6 +184,7 @@ namespace
case OSCTypes::float32: return OSCArgument (readFloat32());
case OSCTypes::string: return OSCArgument (readString());
case OSCTypes::blob: return OSCArgument (readBlob());
case OSCTypes::colour: return OSCArgument (readColour());
default:
// You supplied an invalid OSCType when calling readArgument! This should never happen.
@ -221,8 +214,7 @@ namespace
// bundle, so we know when to consider the next element *not* part of this
// bundle anymore (but part of the outer bundle) and return.
if (input.getNumBytesRemaining() < 16)
throw OSCFormatError ("OSC input stream exhausted while reading bundle");
checkBytesAvailable (16, "OSC input stream exhausted while reading bundle");
if (readString() != "#bundle")
throw OSCFormatError ("OSC input stream format error: bundle does not start with string '#bundle'");
@ -247,8 +239,7 @@ namespace
//==============================================================================
OSCBundle::Element readElement()
{
if (input.getNumBytesRemaining() < 4)
throw OSCFormatError ("OSC input stream exhausted while reading bundle element size");
checkBytesAvailable (4, "OSC input stream exhausted while reading bundle element size");
auto elementSize = (size_t) readInt32();
@ -261,8 +252,7 @@ namespace
//==============================================================================
OSCBundle::Element readElementWithKnownSize (size_t elementSize)
{
if ((uint64) input.getNumBytesRemaining() < elementSize)
throw OSCFormatError ("OSC input stream exhausted while reading bundle element content");
checkBytesAvailable ((int64) elementSize, "OSC input stream exhausted while reading bundle element content");
auto firstContentChar = static_cast<const char*> (getData()) [getPosition()];
@ -312,6 +302,12 @@ namespace
return message;
}
void checkBytesAvailable (int64 requiredBytes, const char* message)
{
if (input.getNumBytesRemaining() < requiredBytes)
throw OSCFormatError (message);
}
};
} // namespace

View file

@ -85,6 +85,11 @@ namespace
return output.writeRepeatedByte (0, numPaddingZeros);
}
bool writeColour (OSCColour colour)
{
return output.writeIntBigEndian ((int32) colour.toInt32());
}
bool writeTimeTag (OSCTimeTag timeTag)
{
return output.writeInt64BigEndian (int64 (timeTag.getRawTimeTag()));
@ -123,6 +128,7 @@ namespace
case OSCTypes::float32: return writeFloat32 (arg.getFloat32());
case OSCTypes::string: return writeString (arg.getString());
case OSCTypes::blob: return writeBlob (arg.getBlob());
case OSCTypes::colour: return writeColour (arg.getColour());
default:
// In this very unlikely case you supplied an invalid OSCType!

View file

@ -26,8 +26,24 @@
namespace juce
{
const OSCType OSCTypes::int32 = 'i';
const OSCType OSCTypes::float32 = 'f';
const OSCType OSCTypes::string = 's';
const OSCType OSCTypes::blob = 'b';
const OSCType OSCTypes::int32 = 'i';
const OSCType OSCTypes::float32 = 'f';
const OSCType OSCTypes::string = 's';
const OSCType OSCTypes::blob = 'b';
const OSCType OSCTypes::colour = 'r';
uint32 OSCColour::toInt32() const
{
return ByteOrder::makeInt (alpha, blue, green, red);
}
OSCColour OSCColour::fromInt32 (uint32 c)
{
return { (uint8) (c >> 24),
(uint8) (c >> 16),
(uint8) (c >> 8),
(uint8) c };
}
} // namespace juce

View file

@ -28,7 +28,6 @@ namespace juce
{
//==============================================================================
/** The type used for OSC type tags. */
using OSCType = char;
@ -37,7 +36,6 @@ using OSCType = char;
using OSCTypeList = Array<OSCType>;
//==============================================================================
/** The definitions of supported OSC types and their associated OSC type tags,
as defined in the OpenSoundControl 1.0 specification.
@ -53,16 +51,34 @@ public:
static const OSCType float32;
static const OSCType string;
static const OSCType blob;
static const OSCType colour;
static bool isSupportedType (OSCType type) noexcept
{
return type == OSCTypes::int32
|| type == OSCTypes::float32
|| type == OSCTypes::string
|| type == OSCTypes::blob;
|| type == OSCTypes::blob
|| type == OSCTypes::colour;
}
};
//==============================================================================
/**
Holds a 32-bit RGBA colour for passing to and from an OSCArgument.
@see OSCArgument, OSCTypes::colour
@tags{OSC}
*/
struct OSCColour
{
uint8 red, green, blue, alpha;
uint32 toInt32() const;
static OSCColour fromInt32 (uint32);
};
//==============================================================================
/** Base class for exceptions that can be thrown by methods in the OSC module.