40 struct OSCOutputStream
42 OSCOutputStream() noexcept {}
45 const void* getData() const noexcept {
return output.getData(); }
48 size_t getDataSize() const noexcept {
return output.getDataSize(); }
51 bool writeInt32 (
int32 value)
53 return output.writeIntBigEndian (value);
56 bool writeUint64 (
uint64 value)
58 return output.writeInt64BigEndian (
int64 (value));
61 bool writeFloat32 (
float value)
63 return output.writeFloatBigEndian (value);
66 bool writeString (
const String& value)
68 if (! output.writeString (value))
71 const size_t numPaddingZeros = ~value.getNumBytesAsUTF8() & 3;
73 return output.writeRepeatedByte (
'\0', numPaddingZeros);
76 bool writeBlob (
const MemoryBlock& blob)
78 if (! (output.writeIntBigEndian ((
int) blob.getSize())
79 && output.write (blob.getData(), blob.getSize())))
82 const size_t numPaddingZeros = ~(blob.getSize() - 1) & 3;
84 return output.writeRepeatedByte (0, numPaddingZeros);
87 bool writeColour (OSCColour colour)
89 return output.writeIntBigEndian ((
int32) colour.toInt32());
92 bool writeTimeTag (OSCTimeTag timeTag)
94 return output.writeInt64BigEndian (
int64 (timeTag.getRawTimeTag()));
97 bool writeAddress (
const OSCAddress& address)
99 return writeString (address.toString());
102 bool writeAddressPattern (
const OSCAddressPattern& ap)
104 return writeString (ap.toString());
107 bool writeTypeTagString (
const OSCTypeList& typeList)
109 output.writeByte (
',');
111 if (typeList.size() > 0)
112 output.write (typeList.begin(), (
size_t) typeList.size());
114 output.writeByte (
'\0');
116 size_t bytesWritten = (
size_t) typeList.size() + 1;
117 size_t numPaddingZeros = ~bytesWritten & 0x03;
119 return output.writeRepeatedByte (
'\0', numPaddingZeros);
122 bool writeArgument (
const OSCArgument& arg)
124 switch (arg.getType())
126 case OSCTypes::int32:
return writeInt32 (arg.getInt32());
127 case OSCTypes::float32:
return writeFloat32 (arg.getFloat32());
128 case OSCTypes::string:
return writeString (arg.getString());
129 case OSCTypes::blob:
return writeBlob (arg.getBlob());
130 case OSCTypes::colour:
return writeColour (arg.getColour());
140 bool writeMessage (
const OSCMessage& msg)
142 if (! writeAddressPattern (msg.getAddressPattern()))
147 for (
auto& arg : msg)
148 typeList.add (arg.getType());
150 if (! writeTypeTagString (typeList))
153 for (
auto& arg : msg)
154 if (! writeArgument (arg))
160 bool writeBundle (
const OSCBundle& bundle)
162 if (! writeString (
"#bundle"))
165 if (! writeTimeTag (bundle.getTimeTag()))
168 for (
auto& element : bundle)
169 if (! writeBundleElement (element))
176 bool writeBundleElement (
const OSCBundle::Element& element)
178 const int64 startPos = output.getPosition();
180 if (! writeInt32 (0))
183 if (element.isBundle())
185 if (! writeBundle (element.getBundle()))
190 if (! writeMessage (element.getMessage()))
194 const int64 endPos = output.getPosition();
195 const int64 elementSize = endPos - (startPos + 4);
197 return output.setPosition (startPos)
198 && writeInt32 ((
int32) elementSize)
199 && output.setPosition (endPos);
203 MemoryOutputStream output;
215 ~Pimpl()
noexcept { disconnect(); }
218 bool connect (
const String& newTargetHost,
int newTargetPort)
224 targetHostName = newTargetHost;
225 targetPortNumber = newTargetPort;
227 if (socket->bindToPort (0))
234 bool connectToSocket (
DatagramSocket& newSocket,
const String& newTargetHost,
int newTargetPort)
239 socket.setNonOwned (&newSocket);
240 targetHostName = newTargetHost;
241 targetPortNumber = newTargetPort;
252 bool send (
const OSCMessage& message,
const String& hostName,
int portNumber)
254 OSCOutputStream outStream;
256 return outStream.writeMessage (message)
257 && sendOutputStream (outStream, hostName, portNumber);
260 bool send (
const OSCBundle& bundle,
const String& hostName,
int portNumber)
262 OSCOutputStream outStream;
264 return outStream.writeBundle (bundle)
265 && sendOutputStream (outStream, hostName, portNumber);
268 bool send (
const OSCMessage& message) {
return send (message, targetHostName, targetPortNumber); }
269 bool send (
const OSCBundle& bundle) {
return send (bundle, targetHostName, targetPortNumber); }
273 bool sendOutputStream (OSCOutputStream& outStream,
const String& hostName,
int portNumber)
275 if (socket !=
nullptr)
277 const int streamSize = (
int) outStream.getDataSize();
279 const int bytesWritten = socket->write (hostName, portNumber,
280 outStream.getData(), streamSize);
281 return bytesWritten == streamSize;
294 int targetPortNumber = 0;
314 return pimpl->connect (targetHostName, targetPortNumber);
319 return pimpl->connectToSocket (
socket, targetHostName, targetPortNumber);
324 return pimpl->disconnect();
339class OSCBinaryWriterTests final :
public UnitTest
342 OSCBinaryWriterTests()
343 :
UnitTest (
"OSCBinaryWriter class", UnitTestCategories::osc)
346 void runTest()
override
348 beginTest (
"writing OSC addresses");
350 OSCOutputStream outStream;
351 const char check[16] = {
'/',
't',
'e',
's',
't',
'/',
'f',
'a',
'd',
'e',
'r',
'7',
'\0',
'\0',
'\0',
'\0' };
353 OSCAddress address (
"/test/fader7");
354 expect (outStream.writeAddress (address));
356 expect (outStream.getDataSize() == sizeof (check));
357 expect (
std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
360 beginTest (
"writing OSC address patterns");
362 OSCOutputStream outStream;
363 const char check[20] = {
'/',
'*',
'/',
'*',
'p',
'u',
't',
'/',
'f',
'a',
'd',
'e',
'r',
'[',
'0',
'-',
'9',
']',
'\0',
'\0' };
365 OSCAddressPattern ap (
"/*/*put/fader[0-9]");
366 expect (outStream.writeAddressPattern (ap));
368 expect (outStream.getDataSize() == sizeof (check));
369 expect (
std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
372 beginTest (
"writing OSC time tags");
374 OSCOutputStream outStream;
375 const char check[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
378 expect (outStream.writeTimeTag (tag));
379 expect (outStream.getDataSize() == 8);
380 expect (
std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
383 beginTest (
"writing OSC type tag strings");
386 OSCOutputStream outStream;
390 const char check[4] = {
',',
'\0',
'\0',
'\0' };
391 expect (outStream.writeTypeTagString (list));
392 expect (outStream.getDataSize() == 4);
393 expect (
std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
397 OSCOutputStream outStream;
400 list.add (OSCTypes::int32);
401 list.add (OSCTypes::float32);
403 const char check[4] = {
',',
'i',
'f',
'\0' };
404 expect (outStream.writeTypeTagString (list));
405 expect (outStream.getDataSize() == sizeof (check));
406 expect (
std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
410 OSCOutputStream outStream;
413 list.add (OSCTypes::blob);
414 list.add (OSCTypes::blob);
415 list.add (OSCTypes::string);
417 const char check[8] = {
',',
'b',
'b',
's',
'\0',
'\0',
'\0',
'\0' };
418 expect (outStream.writeTypeTagString (list));
419 expect (outStream.getDataSize() == sizeof (check));
420 expect (
std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
424 beginTest (
"writing OSC arguments");
428 const uint8 testIntRepresentation[] = { 0xFF, 0xFF, 0xF8, 0x21 };
430 float testFloat = 345.6125f;
431 const uint8 testFloatRepresentation[] = { 0x43, 0xAC, 0xCE, 0x66 };
433 String testString =
"Hello, World!";
434 const char testStringRepresentation[] = {
'H',
'e',
'l',
'l',
'o',
',',
' ',
'W',
'o',
'r',
'l',
'd',
'!',
'\0',
'\0',
'\0' };
436 const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
437 const MemoryBlock testBlob (testBlobData,
sizeof (testBlobData));
438 const uint8 testBlobRepresentation[] = { 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00 };
444 OSCArgument arg (testInt);
445 OSCOutputStream outStream;
447 expect (outStream.writeArgument (arg));
448 expect (outStream.getDataSize() == 4);
449 expect (
std::memcmp (outStream.getData(), testIntRepresentation, sizeof (testIntRepresentation)) == 0);
453 OSCArgument arg (testFloat);
454 OSCOutputStream outStream;
456 expect (outStream.writeArgument (arg));
457 expect (outStream.getDataSize() == 4);
458 expect (
std::memcmp (outStream.getData(), testFloatRepresentation, sizeof (testFloatRepresentation)) == 0);
463 expect (testString.length() % 4 != 0);
464 static_assert (
sizeof (testStringRepresentation) % 4 == 0,
"Size must be a multiple of 4");
466 OSCArgument arg (testString);
467 OSCOutputStream outStream;
469 expect (outStream.writeArgument (arg));
470 expect (outStream.getDataSize() == sizeof (testStringRepresentation));
471 expect (
std::memcmp (outStream.getData(), testStringRepresentation, sizeof (testStringRepresentation)) == 0);
476 expect (testBlob.getSize() % 4 != 0);
477 static_assert (
sizeof (testBlobRepresentation) % 4 == 0,
"Size must be a multiple of 4");
479 OSCArgument arg (testBlob);
480 OSCOutputStream outStream;
482 expect (outStream.writeArgument (arg));
483 expect (outStream.getDataSize() == sizeof (testBlobRepresentation));
484 expect (
std::memcmp (outStream.getData(), testBlobRepresentation, sizeof (testBlobRepresentation)) == 0);
489 beginTest (
"Writing strings with correct padding");
494 OSCArgument with15Chars (
"123456789012345");
495 OSCOutputStream outStream;
496 expect (outStream.writeArgument (with15Chars));
497 expect (outStream.getDataSize() == 16);
500 OSCArgument with16Chars (
"1234567890123456");
501 OSCOutputStream outStream;
502 expect (outStream.writeArgument (with16Chars));
503 expect (outStream.getDataSize() == 20);
506 OSCArgument with17Chars (
"12345678901234567");
507 OSCOutputStream outStream;
508 expect (outStream.writeArgument (with17Chars));
509 expect (outStream.getDataSize() == 20);
513 OSCArgument with18Chars (
"123456789012345678");
514 OSCOutputStream outStream;
515 expect (outStream.writeArgument (with18Chars));
516 expect (outStream.getDataSize() == 20);
520 OSCArgument with19Chars (
"1234567890123456789");
521 OSCOutputStream outStream;
522 expect (outStream.writeArgument (with19Chars));
523 expect (outStream.getDataSize() == 20);
527 OSCArgument with20Chars (
"12345678901234567890");
528 OSCOutputStream outStream;
529 expect (outStream.writeArgument (with20Chars));
530 expect (outStream.getDataSize() == 24);
533 beginTest (
"Writing blobs with correct padding");
535 const char buffer[20] = {};
537 OSCArgument with15Bytes (MemoryBlock (buffer, 15));
538 OSCOutputStream outStream;
539 expect (outStream.writeArgument (with15Bytes));
540 expect (outStream.getDataSize() == 20);
543 OSCArgument with16Bytes (MemoryBlock (buffer, 16));
544 OSCOutputStream outStream;
545 expect (outStream.writeArgument (with16Bytes));
546 expect (outStream.getDataSize() == 20);
549 OSCArgument with17Bytes (MemoryBlock (buffer, 17));
550 OSCOutputStream outStream;
551 expect (outStream.writeArgument (with17Bytes));
552 expect (outStream.getDataSize() == 24);
555 OSCArgument with18Bytes (MemoryBlock (buffer, 18));
556 OSCOutputStream outStream;
557 expect (outStream.writeArgument (with18Bytes));
558 expect (outStream.getDataSize() == 24);
561 OSCArgument with19Bytes (MemoryBlock (buffer, 19));
562 OSCOutputStream outStream;
563 expect (outStream.writeArgument (with19Bytes));
564 expect (outStream.getDataSize() == 24);
567 OSCArgument with20Bytes (MemoryBlock (buffer, 20));
568 OSCOutputStream outStream;
569 expect (outStream.writeArgument (with20Bytes));
570 expect (outStream.getDataSize() == 24);
574 beginTest (
"Writing OSC messages.");
577 int32 testInt = -2015;
578 float testFloat = 345.6125f;
579 String testString =
"Hello, World!";
581 const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
582 const MemoryBlock testBlob (testBlobData,
sizeof (testBlobData));
584 uint8 check[52] = {
'/',
't',
'e',
's',
't',
'\0',
'\0',
'\0',
585 ',',
'i',
'f',
's',
'b',
'\0',
'\0',
'\0',
586 0xFF, 0xFF, 0xF8, 0x21,
587 0x43, 0xAC, 0xCE, 0x66,
588 'H',
'e',
'l',
'l',
'o',
',',
' ',
'W',
'o',
'r',
'l',
'd',
'!',
'\0',
'\0',
'\0',
589 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00
592 OSCOutputStream outStream;
594 OSCMessage msg (
"/test");
596 msg.addInt32 (testInt);
597 msg.addFloat32 (testFloat);
598 msg.addString (testString);
599 msg.addBlob (testBlob);
601 expect (outStream.writeMessage (msg));
602 expect (outStream.getDataSize() == sizeof (check));
603 expect (
std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
607 beginTest (
"Writing OSC bundle.");
610 int32 testInt = -2015;
611 float testFloat = 345.6125f;
612 String testString =
"Hello, World!";
613 const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
614 const MemoryBlock testBlob (testBlobData,
sizeof (testBlobData));
617 '#',
'b',
'u',
'n',
'd',
'l',
'e',
'\0',
618 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
620 0x00, 0x00, 0x00, 0x34,
622 '/',
't',
'e',
's',
't',
'/',
'1',
'\0',
623 ',',
'i',
'f',
's',
'b',
'\0',
'\0',
'\0',
624 0xFF, 0xFF, 0xF8, 0x21,
625 0x43, 0xAC, 0xCE, 0x66,
626 'H',
'e',
'l',
'l',
'o',
',',
' ',
'W',
'o',
'r',
'l',
'd',
'!',
'\0',
'\0',
'\0',
627 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00,
629 0x00, 0x00, 0x00, 0x0C,
631 '/',
't',
'e',
's',
't',
'/',
'2',
'\0',
632 ',',
'\0',
'\0',
'\0',
634 0x00, 0x00, 0x00, 0x10,
636 '#',
'b',
'u',
'n',
'd',
'l',
'e',
'\0',
637 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
640 OSCOutputStream outStream;
644 OSCMessage msg1 (
"/test/1");
645 msg1.addInt32 (testInt);
646 msg1.addFloat32 (testFloat);
647 msg1.addString (testString);
648 msg1.addBlob (testBlob);
649 bundle.addElement (msg1);
651 OSCMessage msg2 (
"/test/2");
652 bundle.addElement (msg2);
655 bundle.addElement (subBundle);
657 expect (outStream.writeBundle (bundle));
658 expect (outStream.getDataSize() == sizeof (check));
659 expect (
std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
665static OSCBinaryWriterTests OSCBinaryWriterUnitTests;
668class OSCRoundTripTests final :
public UnitTest
672 : UnitTest (
"OSCRoundTripTests class", UnitTestCategories::osc)
675 void runTest()
override
677 beginTest (
"Empty OSC message");
679 OSCMessage outMessage (
"/test/empty");
681 OSCOutputStream output;
682 output.writeMessage (outMessage);
684 OSCInputStream input (output.getData(), output.getDataSize());
685 OSCMessage inMessage = input.readMessage();
687 expectEquals (inMessage.size(), 0);
690 beginTest (
"OSC message with single argument");
692 OSCMessage outMessage (
"/test/one_arg", 42);
694 OSCOutputStream output;
695 output.writeMessage (outMessage);
697 OSCInputStream input (output.getData(), output.getDataSize());
698 OSCMessage inMessage = input.readMessage();
700 expectEquals (inMessage.size(), 1);
701 expectEquals (inMessage[0].getInt32(), 42);
704 beginTest (
"OSC message with multiple arguments");
706 OSCMessage outMessage (
"/test/four_args", 42, 0.5f, String (
"foo"), String (
"bar"));
708 OSCOutputStream output;
709 output.writeMessage (outMessage);
711 OSCInputStream input (output.getData(), output.getDataSize());
712 OSCMessage inMessage = input.readMessage();
714 expectEquals (inMessage.size(), 4);
715 expectEquals (inMessage[0].getInt32(), 42);
716 expectEquals (inMessage[1].getFloat32(), 0.5f);
717 expectEquals (inMessage[2].getString(), String (
"foo"));
718 expectEquals (inMessage[3].getString(), String (
"bar"));
721 beginTest (
"Empty OSC bundle");
725 OSCOutputStream output;
726 output.writeBundle (outBundle);
728 OSCInputStream input (output.getData(), output.getDataSize());
729 OSCBundle inBundle = input.readBundle();
731 expectEquals (inBundle.size(), 0);
734 beginTest (
"OSC bundle with single message");
736 OSCMessage outMessage (
"/test/one_arg", 42);
738 outBundle.addElement (outMessage);
740 OSCOutputStream output;
741 output.writeBundle (outBundle);
743 OSCInputStream input (output.getData(), output.getDataSize());
744 OSCBundle inBundle = input.readBundle();
746 expectEquals (inBundle.size(), 1);
748 OSCMessage inMessage = inBundle[0].getMessage();
750 expectEquals (inMessage.getAddressPattern().toString(), String (
"/test/one_arg"));
751 expectEquals (inMessage.size(), 1);
752 expectEquals (inMessage[0].getInt32(), 42);
755 beginTest (
"OSC bundle with multiple messages");
757 OSCMessage outMessage1 (
"/test/empty");
758 OSCMessage outMessage2 (
"/test/one_arg", 42);
759 OSCMessage outMessage3 (
"/test/four_args", 42, 0.5f, String (
"foo"), String (
"bar"));
762 outBundle.addElement (outMessage1);
763 outBundle.addElement (outMessage2);
764 outBundle.addElement (outMessage3);
766 OSCOutputStream output;
767 output.writeBundle (outBundle);
769 OSCInputStream input (output.getData(), output.getDataSize());
770 OSCBundle inBundle = input.readBundle();
772 expectEquals (inBundle.size(), 3);
775 OSCMessage inMessage = inBundle[0].getMessage();
777 expectEquals (inMessage.getAddressPattern().toString(), String (
"/test/empty"));
778 expectEquals (inMessage.size(), 0);
781 OSCMessage inMessage = inBundle[1].getMessage();
783 expectEquals (inMessage.getAddressPattern().toString(), String (
"/test/one_arg"));
784 expectEquals (inMessage.size(), 1);
785 expectEquals (inMessage[0].getInt32(), 42);
788 OSCMessage inMessage = inBundle[2].getMessage();
790 expectEquals (inMessage.getAddressPattern().toString(), String (
"/test/four_args"));
791 expectEquals (inMessage.size(), 4);
792 expectEquals (inMessage[0].getInt32(), 42);
793 expectEquals (inMessage[1].getFloat32(), 0.5f);
794 expectEquals (inMessage[2].getString(), String (
"foo"));
795 expectEquals (inMessage[3].getString(), String (
"bar"));
799 beginTest (
"OSC bundle containing another bundle");
801 OSCBundle outBundleNested;
802 outBundleNested.addElement (OSCMessage (
"/test/one_arg", 42));
805 outBundle.addElement (outBundleNested);
807 OSCOutputStream output;
808 output.writeBundle (outBundle);
810 OSCInputStream input (output.getData(), output.getDataSize());
811 OSCBundle inBundle = input.readBundle();
813 expectEquals (inBundle.size(), 1);
814 expect (inBundle[0].isBundle());
815 OSCBundle inBundleNested = inBundle[0].getBundle();
816 expectEquals (inBundleNested.size(), 1);
817 expect (inBundleNested[0].isMessage());
819 OSCMessage msg = inBundleNested[0].getMessage();
821 expectEquals (msg.getAddressPattern().toString(), String (
"/test/one_arg"));
822 expectEquals (msg.size(), 1);
823 expectEquals (msg[0].getInt32(), 42);
826 beginTest (
"OSC bundle containing multiple other bundles");
828 OSCBundle outBundleNested1;
829 outBundleNested1.addElement (OSCMessage (
"/test/empty"));
830 OSCBundle outBundleNested2;
831 outBundleNested2.addElement (OSCMessage (
"/test/one_arg", 42));
834 outBundle.addElement (outBundleNested1);
835 outBundle.addElement (outBundleNested2);
837 OSCOutputStream output;
838 output.writeBundle (outBundle);
840 OSCInputStream input (output.getData(), output.getDataSize());
841 OSCBundle inBundle = input.readBundle();
843 expectEquals (inBundle.size(), 2);
846 expect (inBundle[0].isBundle());
847 OSCBundle inBundleNested = inBundle[0].getBundle();
848 expectEquals (inBundleNested.size(), 1);
849 expect (inBundleNested[0].isMessage());
851 OSCMessage msg = inBundleNested[0].getMessage();
853 expectEquals (msg.getAddressPattern().toString(), String (
"/test/empty"));
854 expectEquals (msg.size(), 0);
857 expect (inBundle[1].isBundle());
858 OSCBundle inBundleNested = inBundle[1].getBundle();
859 expectEquals (inBundleNested.size(), 1);
860 expect (inBundleNested[0].isMessage());
862 OSCMessage msg = inBundleNested[0].getMessage();
864 expectEquals (msg.getAddressPattern().toString(), String (
"/test/one_arg"));
865 expectEquals (msg.size(), 1);
866 expectEquals (msg[0].getInt32(), 42);
872static OSCRoundTripTests OSCRoundTripUnitTests;
A wrapper for a datagram (UDP) socket.
bool connectToSocket(DatagramSocket &socket, const String &targetHostName, int targetPortNumber)
Uses an existing datagram socket for sending OSC packets to the specified target.
bool disconnect()
Disconnects from the currently used UDP port.
OSCSender()
Constructs a new OSCSender.
bool connect(const String &targetHostName, int targetPortNumber)
Connects to a datagram socket and prepares the socket for sending OSC packets to the specified target...
bool sendToIPAddress(const String &targetIPAddress, int targetPortNumber, const OSCMessage &message)
Sends an OSC message to a specific IP address and port.
bool send(const OSCMessage &message)
Sends an OSC message to the target.
Holds a pointer to an object which can optionally be deleted when this pointer goes out of scope.
This is a base class for classes that perform a unit test.
signed int int32
A platform-independent 32-bit signed integer type.
Array< OSCType > OSCTypeList
The type used for OSC type tag strings.
unsigned long long uint64
A platform-independent 64-bit unsigned integer type.
long long int64
A platform-independent 64-bit integer type.