1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 namespace
30 {
31     //==============================================================================
32     /** Writes OSC data to an internal memory buffer, which grows as required.
33 
34         The data that was written into the stream can then be accessed later as
35         a contiguous block of memory.
36 
37         This class implements the Open Sound Control 1.0 Specification for
38         the format in which the OSC data will be written into the buffer.
39     */
40     struct OSCOutputStream
41     {
OSCOutputStreamjuce::__anon5802a7350111::OSCOutputStream42         OSCOutputStream() noexcept {}
43 
44         /** Returns a pointer to the data that has been written to the stream. */
getDatajuce::__anon5802a7350111::OSCOutputStream45         const void* getData() const noexcept    { return output.getData(); }
46 
47         /** Returns the number of bytes of data that have been written to the stream. */
getDataSizejuce::__anon5802a7350111::OSCOutputStream48         size_t getDataSize() const noexcept     { return output.getDataSize(); }
49 
50         //==============================================================================
writeInt32juce::__anon5802a7350111::OSCOutputStream51         bool writeInt32 (int32 value)
52         {
53             return output.writeIntBigEndian (value);
54         }
55 
writeUint64juce::__anon5802a7350111::OSCOutputStream56         bool writeUint64 (uint64 value)
57         {
58             return output.writeInt64BigEndian (int64 (value));
59         }
60 
writeFloat32juce::__anon5802a7350111::OSCOutputStream61         bool writeFloat32 (float value)
62         {
63             return output.writeFloatBigEndian (value);
64         }
65 
writeStringjuce::__anon5802a7350111::OSCOutputStream66         bool writeString (const String& value)
67         {
68             if (! output.writeString (value))
69                 return false;
70 
71             const size_t numPaddingZeros = ~value.getNumBytesAsUTF8() & 3;
72 
73             return output.writeRepeatedByte ('\0', numPaddingZeros);
74         }
75 
writeBlobjuce::__anon5802a7350111::OSCOutputStream76         bool writeBlob (const MemoryBlock& blob)
77         {
78             if (! (output.writeIntBigEndian ((int) blob.getSize())
79                     && output.write (blob.getData(), blob.getSize())))
80                 return false;
81 
82             const size_t numPaddingZeros = ~(blob.getSize() - 1) & 3;
83 
84             return output.writeRepeatedByte (0, numPaddingZeros);
85         }
86 
writeColourjuce::__anon5802a7350111::OSCOutputStream87         bool writeColour (OSCColour colour)
88         {
89             return output.writeIntBigEndian ((int32) colour.toInt32());
90         }
91 
writeTimeTagjuce::__anon5802a7350111::OSCOutputStream92         bool writeTimeTag (OSCTimeTag timeTag)
93         {
94             return output.writeInt64BigEndian (int64 (timeTag.getRawTimeTag()));
95         }
96 
writeAddressjuce::__anon5802a7350111::OSCOutputStream97         bool writeAddress (const OSCAddress& address)
98         {
99             return writeString (address.toString());
100         }
101 
writeAddressPatternjuce::__anon5802a7350111::OSCOutputStream102         bool writeAddressPattern (const OSCAddressPattern& ap)
103         {
104             return writeString (ap.toString());
105         }
106 
writeTypeTagStringjuce::__anon5802a7350111::OSCOutputStream107         bool writeTypeTagString (const OSCTypeList& typeList)
108         {
109             output.writeByte (',');
110 
111             if (typeList.size() > 0)
112                 output.write (typeList.begin(), (size_t) typeList.size());
113 
114             output.writeByte ('\0');
115 
116             size_t bytesWritten = (size_t) typeList.size() + 1;
117             size_t numPaddingZeros = ~bytesWritten & 0x03;
118 
119             return output.writeRepeatedByte ('\0', numPaddingZeros);
120         }
121 
writeArgumentjuce::__anon5802a7350111::OSCOutputStream122         bool writeArgument (const OSCArgument& arg)
123         {
124             switch (arg.getType())
125             {
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());
131 
132                 default:
133                     // In this very unlikely case you supplied an invalid OSCType!
134                     jassertfalse;
135                     return false;
136             }
137         }
138 
139         //==============================================================================
writeMessagejuce::__anon5802a7350111::OSCOutputStream140         bool writeMessage (const OSCMessage& msg)
141         {
142             if (! writeAddressPattern (msg.getAddressPattern()))
143                 return false;
144 
145             OSCTypeList typeList;
146 
147             for (auto& arg : msg)
148                 typeList.add (arg.getType());
149 
150             if (! writeTypeTagString (typeList))
151                 return false;
152 
153             for (auto& arg : msg)
154                 if (! writeArgument (arg))
155                     return false;
156 
157             return true;
158         }
159 
writeBundlejuce::__anon5802a7350111::OSCOutputStream160         bool writeBundle (const OSCBundle& bundle)
161         {
162             if (! writeString ("#bundle"))
163                 return false;
164 
165             if (! writeTimeTag (bundle.getTimeTag()))
166                 return false;
167 
168             for (auto& element : bundle)
169                 if (! writeBundleElement (element))
170                     return false;
171 
172             return true;
173         }
174 
175         //==============================================================================
writeBundleElementjuce::__anon5802a7350111::OSCOutputStream176         bool writeBundleElement (const OSCBundle::Element& element)
177         {
178             const int64 startPos = output.getPosition();
179 
180             if (! writeInt32 (0))   // writing dummy value for element size
181                 return false;
182 
183             if (element.isBundle())
184             {
185                 if (! writeBundle (element.getBundle()))
186                     return false;
187             }
188             else
189             {
190                 if (! writeMessage (element.getMessage()))
191                     return false;
192             }
193 
194             const int64 endPos = output.getPosition();
195             const int64 elementSize = endPos - (startPos + 4);
196 
197             return output.setPosition (startPos)
198                      && writeInt32 ((int32) elementSize)
199                      && output.setPosition (endPos);
200         }
201 
202     private:
203         MemoryOutputStream output;
204 
205         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCOutputStream)
206     };
207 
208 } // namespace
209 
210 
211 //==============================================================================
212 struct OSCSender::Pimpl
213 {
Pimpljuce::OSCSender::Pimpl214     Pimpl() noexcept  {}
~Pimpljuce::OSCSender::Pimpl215     ~Pimpl() noexcept { disconnect(); }
216 
217     //==============================================================================
connectjuce::OSCSender::Pimpl218     bool connect (const String& newTargetHost, int newTargetPort)
219     {
220         if (! disconnect())
221             return false;
222 
223         socket.setOwned (new DatagramSocket (true));
224         targetHostName = newTargetHost;
225         targetPortNumber = newTargetPort;
226 
227         if (socket->bindToPort (0)) // 0 = use any local port assigned by the OS.
228             return true;
229 
230         socket.reset();
231         return false;
232     }
233 
connectToSocketjuce::OSCSender::Pimpl234     bool connectToSocket (DatagramSocket& newSocket, const String& newTargetHost, int newTargetPort)
235     {
236         if (! disconnect())
237             return false;
238 
239         socket.setNonOwned (&newSocket);
240         targetHostName = newTargetHost;
241         targetPortNumber = newTargetPort;
242         return true;
243     }
244 
disconnectjuce::OSCSender::Pimpl245     bool disconnect()
246     {
247         socket.reset();
248         return true;
249     }
250 
251     //==============================================================================
sendjuce::OSCSender::Pimpl252     bool send (const OSCMessage& message, const String& hostName, int portNumber)
253     {
254         OSCOutputStream outStream;
255 
256         return outStream.writeMessage (message)
257             && sendOutputStream (outStream, hostName, portNumber);
258     }
259 
sendjuce::OSCSender::Pimpl260     bool send (const OSCBundle& bundle, const String& hostName, int portNumber)
261     {
262         OSCOutputStream outStream;
263 
264         return outStream.writeBundle (bundle)
265             && sendOutputStream (outStream, hostName, portNumber);
266     }
267 
sendjuce::OSCSender::Pimpl268     bool send (const OSCMessage& message)   { return send (message, targetHostName, targetPortNumber); }
sendjuce::OSCSender::Pimpl269     bool send (const OSCBundle& bundle)     { return send (bundle,  targetHostName, targetPortNumber); }
270 
271 private:
272     //==============================================================================
sendOutputStreamjuce::OSCSender::Pimpl273     bool sendOutputStream (OSCOutputStream& outStream, const String& hostName, int portNumber)
274     {
275         if (socket != nullptr)
276         {
277             const int streamSize = (int) outStream.getDataSize();
278 
279             const int bytesWritten = socket->write (hostName, portNumber,
280                                                     outStream.getData(), streamSize);
281             return bytesWritten == streamSize;
282         }
283 
284         // if you hit this, you tried to send some OSC data without being
285         // connected to a port! You should call OSCSender::connect() first.
286         jassertfalse;
287 
288         return false;
289     }
290 
291     //==============================================================================
292     OptionalScopedPointer<DatagramSocket> socket;
293     String targetHostName;
294     int targetPortNumber = 0;
295 
296     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
297 };
298 
299 
300 //==============================================================================
OSCSender()301 OSCSender::OSCSender()   : pimpl (new Pimpl())
302 {
303 }
304 
~OSCSender()305 OSCSender::~OSCSender()
306 {
307     pimpl->disconnect();
308     pimpl.reset();
309 }
310 
311 //==============================================================================
connect(const String & targetHostName,int targetPortNumber)312 bool OSCSender::connect (const String& targetHostName, int targetPortNumber)
313 {
314     return pimpl->connect (targetHostName, targetPortNumber);
315 }
316 
connectToSocket(DatagramSocket & socket,const String & targetHostName,int targetPortNumber)317 bool OSCSender::connectToSocket (DatagramSocket& socket, const String& targetHostName, int targetPortNumber)
318 {
319     return pimpl->connectToSocket (socket, targetHostName, targetPortNumber);
320 }
321 
disconnect()322 bool OSCSender::disconnect()
323 {
324     return pimpl->disconnect();
325 }
326 
327 //==============================================================================
send(const OSCMessage & message)328 bool OSCSender::send (const OSCMessage& message)    { return pimpl->send (message); }
send(const OSCBundle & bundle)329 bool OSCSender::send (const OSCBundle& bundle)      { return pimpl->send (bundle); }
330 
sendToIPAddress(const String & host,int port,const OSCMessage & message)331 bool OSCSender::sendToIPAddress (const String& host, int port, const OSCMessage& message) { return pimpl->send (message, host, port); }
sendToIPAddress(const String & host,int port,const OSCBundle & bundle)332 bool OSCSender::sendToIPAddress (const String& host, int port, const OSCBundle& bundle)   { return pimpl->send (bundle,  host, port); }
333 
334 
335 //==============================================================================
336 //==============================================================================
337 #if JUCE_UNIT_TESTS
338 
339 class OSCBinaryWriterTests  : public UnitTest
340 {
341 public:
OSCBinaryWriterTests()342     OSCBinaryWriterTests()
343         : UnitTest ("OSCBinaryWriter class", UnitTestCategories::osc)
344     {}
345 
runTest()346     void runTest()
347     {
348         beginTest ("writing OSC addresses");
349         {
350             OSCOutputStream outStream;
351             const char check[16] = { '/', 't', 'e', 's', 't', '/', 'f', 'a', 'd', 'e', 'r', '7', '\0', '\0', '\0', '\0' };
352 
353             OSCAddress address ("/test/fader7");
354             expect (outStream.writeAddress (address));
355 
356             expect (outStream.getDataSize() == sizeof (check));
357             expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
358         }
359 
360         beginTest ("writing OSC address patterns");
361         {
362             OSCOutputStream outStream;
363             const char check[20] = { '/', '*', '/', '*', 'p', 'u', 't', '/', 'f', 'a', 'd', 'e', 'r', '[', '0', '-', '9', ']', '\0', '\0' };
364 
365             OSCAddressPattern ap ("/*/*put/fader[0-9]");
366             expect (outStream.writeAddressPattern (ap));
367 
368             expect (outStream.getDataSize() == sizeof (check));
369             expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
370         }
371 
372         beginTest ("writing OSC time tags");
373         {
374             OSCOutputStream outStream;
375             const char check[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
376             OSCTimeTag tag;
377 
378             expect (outStream.writeTimeTag (tag));
379             expect (outStream.getDataSize() == 8);
380             expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
381         }
382 
383         beginTest ("writing OSC type tag strings");
384         {
385             {
386                 OSCOutputStream outStream;
387 
388                 OSCTypeList list;
389 
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);
394             }
395 
396             {
397                 OSCOutputStream outStream;
398 
399                 OSCTypeList list;
400                 list.add (OSCTypes::int32);
401                 list.add (OSCTypes::float32);
402 
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);
407             }
408 
409             {
410                 OSCOutputStream outStream;
411 
412                 OSCTypeList list;
413                 list.add (OSCTypes::blob);
414                 list.add (OSCTypes::blob);
415                 list.add (OSCTypes::string);
416 
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);
421             }
422         }
423 
424         beginTest ("writing OSC arguments");
425         {
426             // test data:
427             int testInt = -2015;
428             const uint8 testIntRepresentation[] =  { 0xFF, 0xFF, 0xF8, 0x21 }; // big endian two's complement
429 
430             float testFloat = 345.6125f;
431             const uint8 testFloatRepresentation[] = { 0x43, 0xAC, 0xCE, 0x66 }; // big endian IEEE 754
432 
433             String testString = "Hello, World!";
434             const char testStringRepresentation[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0' }; // padded to size % 4 == 0
435 
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 }; // padded to size % 4 == 0
439 
440             // write:
441 
442             {
443                 // int32:
444                 OSCArgument arg (testInt);
445                 OSCOutputStream outStream;
446 
447                 expect (outStream.writeArgument (arg));
448                 expect (outStream.getDataSize() == 4);
449                 expect (std::memcmp (outStream.getData(), testIntRepresentation, sizeof (testIntRepresentation)) == 0);
450             }
451             {
452                 // float32:
453                 OSCArgument arg (testFloat);
454                 OSCOutputStream outStream;
455 
456                 expect (outStream.writeArgument (arg));
457                 expect (outStream.getDataSize() == 4);
458                 expect (std::memcmp (outStream.getData(), testFloatRepresentation, sizeof (testFloatRepresentation)) == 0);
459 
460             }
461             {
462                 // string:
463                 expect (testString.length() % 4 != 0); // check whether we actually cover padding
464                 expect (sizeof (testStringRepresentation) % 4 == 0);
465 
466                 OSCArgument arg (testString);
467                 OSCOutputStream outStream;
468 
469                 expect (outStream.writeArgument (arg));
470                 expect (outStream.getDataSize() == sizeof (testStringRepresentation));
471                 expect (std::memcmp (outStream.getData(), testStringRepresentation, sizeof (testStringRepresentation)) == 0);
472 
473             }
474             {
475                 // blob:
476                 expect (testBlob.getSize() % 4 != 0);  // check whether we actually cover padding
477                 expect (sizeof (testBlobRepresentation) % 4 == 0);
478 
479                 OSCArgument arg (testBlob);
480                 OSCOutputStream outStream;
481 
482                 expect (outStream.writeArgument (arg));
483                 expect (outStream.getDataSize() == sizeof (testBlobRepresentation));
484                 expect (std::memcmp (outStream.getData(), testBlobRepresentation, sizeof (testBlobRepresentation)) == 0);
485 
486             }
487         }
488 
489         beginTest ("Writing strings with correct padding");
490         {
491             // the only OSC-specific thing to check is the correct number of padding zeros
492 
493             {
494                 OSCArgument with15Chars ("123456789012345");
495                 OSCOutputStream outStream;
496                 expect (outStream.writeArgument (with15Chars));
497                 expect (outStream.getDataSize() == 16);
498             }
499             {
500                 OSCArgument with16Chars ("1234567890123456");
501                 OSCOutputStream outStream;
502                 expect (outStream.writeArgument (with16Chars));
503                 expect (outStream.getDataSize() == 20);
504             }
505             {
506                 OSCArgument with17Chars ("12345678901234567");
507                 OSCOutputStream outStream;
508                 expect (outStream.writeArgument (with17Chars));
509                 expect (outStream.getDataSize() == 20);
510             }
511             {
512 
513                 OSCArgument with18Chars ("123456789012345678");
514                 OSCOutputStream outStream;
515                 expect (outStream.writeArgument (with18Chars));
516                 expect (outStream.getDataSize() == 20);
517             }
518             {
519 
520                 OSCArgument with19Chars ("1234567890123456789");
521                 OSCOutputStream outStream;
522                 expect (outStream.writeArgument (with19Chars));
523                 expect (outStream.getDataSize() == 20);
524             }
525             {
526 
527                 OSCArgument with20Chars ("12345678901234567890");
528                 OSCOutputStream outStream;
529                 expect (outStream.writeArgument (with20Chars));
530                 expect (outStream.getDataSize() == 24);
531             }
532         }
533         beginTest ("Writing blobs with correct padding");
534         {
535             const char buffer[20] = {};
536             {
537                 OSCArgument with15Bytes (MemoryBlock (buffer, 15));
538                 OSCOutputStream outStream;
539                 expect (outStream.writeArgument (with15Bytes));
540                 expect (outStream.getDataSize() == 20);
541             }
542             {
543                 OSCArgument with16Bytes (MemoryBlock (buffer, 16));
544                 OSCOutputStream outStream;
545                 expect (outStream.writeArgument (with16Bytes));
546                 expect (outStream.getDataSize() == 20);
547             }
548             {
549                 OSCArgument with17Bytes (MemoryBlock (buffer, 17));
550                 OSCOutputStream outStream;
551                 expect (outStream.writeArgument (with17Bytes));
552                 expect (outStream.getDataSize() == 24);
553             }
554             {
555                 OSCArgument with18Bytes (MemoryBlock (buffer, 18));
556                 OSCOutputStream outStream;
557                 expect (outStream.writeArgument (with18Bytes));
558                 expect (outStream.getDataSize() == 24);
559             }
560             {
561                 OSCArgument with19Bytes (MemoryBlock (buffer, 19));
562                 OSCOutputStream outStream;
563                 expect (outStream.writeArgument (with19Bytes));
564                 expect (outStream.getDataSize() == 24);
565             }
566             {
567                 OSCArgument with20Bytes (MemoryBlock (buffer, 20));
568                 OSCOutputStream outStream;
569                 expect (outStream.writeArgument (with20Bytes));
570                 expect (outStream.getDataSize() == 24);
571             }
572         }
573 
574         beginTest ("Writing OSC messages.");
575         {
576             {
577                 int32 testInt = -2015;
578                 float testFloat = 345.6125f;
579                 String testString = "Hello, World!";
580 
581                 const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
582                 const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
583 
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
590                 };
591 
592                 OSCOutputStream outStream;
593 
594                 OSCMessage msg ("/test");
595 
596                 msg.addInt32 (testInt);
597                 msg.addFloat32 (testFloat);
598                 msg.addString (testString);
599                 msg.addBlob (testBlob);
600 
601                 expect (outStream.writeMessage (msg));
602                 expect (outStream.getDataSize() == sizeof (check));
603                 expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
604             }
605         }
606 
607         beginTest ("Writing OSC bundle.");
608         {
609             {
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));
615 
616                 uint8 check[] = {
617                     '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
618                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
619 
620                     0x00, 0x00, 0x00, 0x34,
621 
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,
628 
629                     0x00, 0x00, 0x00, 0x0C,
630 
631                     '/', 't', 'e', 's', 't', '/', '2', '\0',
632                     ',', '\0', '\0', '\0',
633 
634                     0x00, 0x00, 0x00, 0x10,
635 
636                     '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
637                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
638                 };
639 
640                 OSCOutputStream outStream;
641 
642                 OSCBundle bundle;
643 
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);
650 
651                 OSCMessage msg2 ("/test/2");
652                 bundle.addElement (msg2);
653 
654                 OSCBundle subBundle;
655                 bundle.addElement (subBundle);
656 
657                 expect (outStream.writeBundle (bundle));
658                 expect (outStream.getDataSize() == sizeof (check));
659                 expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
660             }
661         }
662     }
663 };
664 
665 static OSCBinaryWriterTests OSCBinaryWriterUnitTests;
666 
667 //==============================================================================
668 class OSCRoundTripTests  : public UnitTest
669 {
670 public:
OSCRoundTripTests()671     OSCRoundTripTests()
672         : UnitTest ("OSCRoundTripTests class", UnitTestCategories::osc)
673     {}
674 
runTest()675     void runTest()
676     {
677         beginTest ("Empty OSC message");
678         {
679             OSCMessage outMessage ("/test/empty");
680 
681             OSCOutputStream output;
682             output.writeMessage (outMessage);
683 
684             OSCInputStream input (output.getData(), output.getDataSize());
685             OSCMessage inMessage = input.readMessage();
686 
687             expectEquals (inMessage.size(), 0);
688         }
689 
690         beginTest ("OSC message with single argument");
691         {
692             OSCMessage outMessage ("/test/one_arg", 42);
693 
694             OSCOutputStream output;
695             output.writeMessage (outMessage);
696 
697             OSCInputStream input (output.getData(), output.getDataSize());
698             OSCMessage inMessage = input.readMessage();
699 
700             expectEquals (inMessage.size(), 1);
701             expectEquals (inMessage[0].getInt32(), 42);
702         }
703 
704         beginTest ("OSC message with multiple arguments");
705         {
706             OSCMessage outMessage ("/test/four_args", 42, 0.5f, String ("foo"), String ("bar"));
707 
708             OSCOutputStream output;
709             output.writeMessage (outMessage);
710 
711             OSCInputStream input (output.getData(), output.getDataSize());
712             OSCMessage inMessage = input.readMessage();
713 
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"));
719         }
720 
721         beginTest ("Empty OSC bundle");
722         {
723             OSCBundle outBundle;
724 
725             OSCOutputStream output;
726             output.writeBundle (outBundle);
727 
728             OSCInputStream input (output.getData(), output.getDataSize());
729             OSCBundle inBundle = input.readBundle();
730 
731             expectEquals (inBundle.size(), 0);
732         }
733 
734         beginTest ("OSC bundle with single message");
735         {
736             OSCMessage outMessage ("/test/one_arg", 42);
737             OSCBundle outBundle;
738             outBundle.addElement (outMessage);
739 
740             OSCOutputStream output;
741             output.writeBundle (outBundle);
742 
743             OSCInputStream input (output.getData(), output.getDataSize());
744             OSCBundle inBundle = input.readBundle();
745 
746             expectEquals (inBundle.size(), 1);
747 
748             OSCMessage inMessage = inBundle[0].getMessage();
749 
750             expectEquals (inMessage.getAddressPattern().toString(), String ("/test/one_arg"));
751             expectEquals (inMessage.size(), 1);
752             expectEquals (inMessage[0].getInt32(), 42);
753         }
754 
755         beginTest ("OSC bundle with multiple messages");
756         {
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"));
760 
761             OSCBundle outBundle;
762             outBundle.addElement (outMessage1);
763             outBundle.addElement (outMessage2);
764             outBundle.addElement (outMessage3);
765 
766             OSCOutputStream output;
767             output.writeBundle (outBundle);
768 
769             OSCInputStream input (output.getData(), output.getDataSize());
770             OSCBundle inBundle = input.readBundle();
771 
772             expectEquals (inBundle.size(), 3);
773 
774             {
775                 OSCMessage inMessage = inBundle[0].getMessage();
776 
777                 expectEquals (inMessage.getAddressPattern().toString(), String ("/test/empty"));
778                 expectEquals (inMessage.size(), 0);
779             }
780             {
781                 OSCMessage inMessage = inBundle[1].getMessage();
782 
783                 expectEquals (inMessage.getAddressPattern().toString(), String ("/test/one_arg"));
784                 expectEquals (inMessage.size(), 1);
785                 expectEquals (inMessage[0].getInt32(), 42);
786             }
787             {
788                 OSCMessage inMessage = inBundle[2].getMessage();
789 
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"));
796             }
797         }
798 
799         beginTest ("OSC bundle containing another bundle");
800         {
801             OSCBundle outBundleNested;
802             outBundleNested.addElement (OSCMessage ("/test/one_arg", 42));
803 
804             OSCBundle outBundle;
805             outBundle.addElement (outBundleNested);
806 
807             OSCOutputStream output;
808             output.writeBundle (outBundle);
809 
810             OSCInputStream input (output.getData(), output.getDataSize());
811             OSCBundle inBundle = input.readBundle();
812 
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());
818 
819             OSCMessage msg = inBundleNested[0].getMessage();
820 
821             expectEquals (msg.getAddressPattern().toString(), String ("/test/one_arg"));
822             expectEquals (msg.size(), 1);
823             expectEquals (msg[0].getInt32(), 42);
824         }
825 
826         beginTest ("OSC bundle containing multiple other bundles");
827         {
828             OSCBundle outBundleNested1;
829             outBundleNested1.addElement (OSCMessage ("/test/empty"));
830             OSCBundle outBundleNested2;
831             outBundleNested2.addElement (OSCMessage ("/test/one_arg", 42));
832 
833             OSCBundle outBundle;
834             outBundle.addElement (outBundleNested1);
835             outBundle.addElement (outBundleNested2);
836 
837             OSCOutputStream output;
838             output.writeBundle (outBundle);
839 
840             OSCInputStream input (output.getData(), output.getDataSize());
841             OSCBundle inBundle = input.readBundle();
842 
843             expectEquals (inBundle.size(), 2);
844 
845             {
846                 expect (inBundle[0].isBundle());
847                 OSCBundle inBundleNested = inBundle[0].getBundle();
848                 expectEquals (inBundleNested.size(), 1);
849                 expect (inBundleNested[0].isMessage());
850 
851                 OSCMessage msg = inBundleNested[0].getMessage();
852 
853                 expectEquals (msg.getAddressPattern().toString(), String ("/test/empty"));
854                 expectEquals (msg.size(), 0);
855             }
856             {
857                 expect (inBundle[1].isBundle());
858                 OSCBundle inBundleNested = inBundle[1].getBundle();
859                 expectEquals (inBundleNested.size(), 1);
860                 expect (inBundleNested[0].isMessage());
861 
862                 OSCMessage msg = inBundleNested[0].getMessage();
863 
864                 expectEquals (msg.getAddressPattern().toString(), String ("/test/one_arg"));
865                 expectEquals (msg.size(), 1);
866                 expectEquals (msg[0].getInt32(), 42);
867             }
868         }
869     }
870 };
871 
872 static OSCRoundTripTests OSCRoundTripUnitTests;
873 
874 #endif
875 
876 } // namespace juce
877