diff --git a/modules/juce_osc/osc/juce_OSCArgument.cpp b/modules/juce_osc/osc/juce_OSCArgument.cpp index 6dabbf277d..4d968354f1 100644 --- a/modules/juce_osc/osc/juce_OSCArgument.cpp +++ b/modules/juce_osc/osc/juce_OSCArgument.cpp @@ -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 (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"); { { diff --git a/modules/juce_osc/osc/juce_OSCArgument.h b/modules/juce_osc/osc/juce_OSCArgument.h index 9492e13dcc..222054cdaf 100644 --- a/modules/juce_osc/osc/juce_OSCArgument.h +++ b/modules/juce_osc/osc/juce_OSCArgument.h @@ -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: //============================================================================== diff --git a/modules/juce_osc/osc/juce_OSCMessage.cpp b/modules/juce_osc/osc/juce_OSCMessage.cpp index 3a4481d9db..44d77e9e9d 100644 --- a/modules/juce_osc/osc/juce_OSCMessage.cpp +++ b/modules/juce_osc/osc/juce_OSCMessage.cpp @@ -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 (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()); } diff --git a/modules/juce_osc/osc/juce_OSCMessage.h b/modules/juce_osc/osc/juce_OSCMessage.h index 448c672462..808203617b 100644 --- a/modules/juce_osc/osc/juce_OSCMessage.h +++ b/modules/juce_osc/osc/juce_OSCMessage.h @@ -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. diff --git a/modules/juce_osc/osc/juce_OSCReceiver.cpp b/modules/juce_osc/osc/juce_OSCReceiver.cpp index b07bf88a00..19a6c6ee0b 100644 --- a/modules/juce_osc/osc/juce_OSCReceiver.cpp +++ b/modules/juce_osc/osc/juce_OSCReceiver.cpp @@ -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 (getData()) [getPosition()]; @@ -312,6 +302,12 @@ namespace return message; } + + void checkBytesAvailable (int64 requiredBytes, const char* message) + { + if (input.getNumBytesRemaining() < requiredBytes) + throw OSCFormatError (message); + } }; } // namespace diff --git a/modules/juce_osc/osc/juce_OSCSender.cpp b/modules/juce_osc/osc/juce_OSCSender.cpp index 29976af9c2..6b76d191ac 100644 --- a/modules/juce_osc/osc/juce_OSCSender.cpp +++ b/modules/juce_osc/osc/juce_OSCSender.cpp @@ -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! diff --git a/modules/juce_osc/osc/juce_OSCTypes.cpp b/modules/juce_osc/osc/juce_OSCTypes.cpp index a78f21e28e..2a74f15ee5 100644 --- a/modules/juce_osc/osc/juce_OSCTypes.cpp +++ b/modules/juce_osc/osc/juce_OSCTypes.cpp @@ -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 diff --git a/modules/juce_osc/osc/juce_OSCTypes.h b/modules/juce_osc/osc/juce_OSCTypes.h index 990879baac..bde84a9694 100644 --- a/modules/juce_osc/osc/juce_OSCTypes.h +++ b/modules/juce_osc/osc/juce_OSCTypes.h @@ -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; //============================================================================== - /** 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.