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