The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

852 lines
31KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. namespace
  22. {
  23. //==============================================================================
  24. /** Writes OSC data to an internal memory buffer, which grows as required.
  25. The data that was written into the stream can then be accessed later as
  26. a contiguous block of memory.
  27. This class implements the Open Sound Control 1.0 Specification for
  28. the format in which the OSC data will be written into the buffer.
  29. */
  30. struct OSCOutputStream
  31. {
  32. OSCOutputStream() noexcept {}
  33. /** Returns a pointer to the data that has been written to the stream. */
  34. const void* getData() const noexcept { return output.getData(); }
  35. /** Returns the number of bytes of data that have been written to the stream. */
  36. size_t getDataSize() const noexcept { return output.getDataSize(); }
  37. //==============================================================================
  38. bool writeInt32 (int32 value)
  39. {
  40. return output.writeIntBigEndian (value);
  41. }
  42. bool writeUint64 (uint64 value)
  43. {
  44. return output.writeInt64BigEndian (int64 (value));
  45. }
  46. bool writeFloat32 (float value)
  47. {
  48. return output.writeFloatBigEndian (value);
  49. }
  50. bool writeString (const String& value)
  51. {
  52. if (! output.writeString (value))
  53. return false;
  54. const size_t numPaddingZeros = ~value.length() & 3;
  55. return output.writeRepeatedByte ('\0', numPaddingZeros);
  56. }
  57. bool writeBlob (const MemoryBlock& blob)
  58. {
  59. if (! (output.writeIntBigEndian ((int) blob.getSize())
  60. && output.write (blob.getData(), blob.getSize())))
  61. return false;
  62. const size_t numPaddingZeros = ~(blob.getSize() - 1) & 3;
  63. return output.writeRepeatedByte (0, numPaddingZeros);
  64. }
  65. bool writeTimeTag (OSCTimeTag timeTag)
  66. {
  67. return output.writeInt64BigEndian (int64 (timeTag.getRawTimeTag()));
  68. }
  69. bool writeAddress (const OSCAddress& address)
  70. {
  71. return writeString (address.toString());
  72. }
  73. bool writeAddressPattern (const OSCAddressPattern& ap)
  74. {
  75. return writeString (ap.toString());
  76. }
  77. bool writeTypeTagString (const OSCTypeList& typeList)
  78. {
  79. output.writeByte (',');
  80. if (typeList.size() > 0)
  81. output.write (typeList.begin(), (size_t) typeList.size());
  82. output.writeByte ('\0');
  83. size_t bytesWritten = (size_t) typeList.size() + 1;
  84. size_t numPaddingZeros = ~bytesWritten & 0x03;
  85. return output.writeRepeatedByte ('\0', numPaddingZeros);
  86. }
  87. bool writeArgument (const OSCArgument& arg)
  88. {
  89. switch (arg.getType())
  90. {
  91. case OSCTypes::int32: return writeInt32 (arg.getInt32());
  92. case OSCTypes::float32: return writeFloat32 (arg.getFloat32());
  93. case OSCTypes::string: return writeString (arg.getString());
  94. case OSCTypes::blob: return writeBlob (arg.getBlob());
  95. default:
  96. // In this very unlikely case you supplied an invalid OSCType!
  97. jassertfalse;
  98. return false;
  99. }
  100. }
  101. //==============================================================================
  102. bool writeMessage (const OSCMessage& msg)
  103. {
  104. if (! writeAddressPattern (msg.getAddressPattern()))
  105. return false;
  106. OSCTypeList typeList;
  107. for (auto& arg : msg)
  108. typeList.add (arg.getType());
  109. if (! writeTypeTagString (typeList))
  110. return false;
  111. for (auto& arg : msg)
  112. if (! writeArgument (arg))
  113. return false;
  114. return true;
  115. }
  116. bool writeBundle (const OSCBundle& bundle)
  117. {
  118. if (! writeString ("#bundle"))
  119. return false;
  120. if (! writeTimeTag (bundle.getTimeTag()))
  121. return false;
  122. for (auto& element : bundle)
  123. if (! writeBundleElement (element))
  124. return false;
  125. return true;
  126. }
  127. //==============================================================================
  128. bool writeBundleElement (const OSCBundle::Element& element)
  129. {
  130. const int64 startPos = output.getPosition();
  131. if (! writeInt32 (0)) // writing dummy value for element size
  132. return false;
  133. if (element.isBundle())
  134. {
  135. if (! writeBundle (element.getBundle()))
  136. return false;
  137. }
  138. else
  139. {
  140. if (! writeMessage (element.getMessage()))
  141. return false;
  142. }
  143. const int64 endPos = output.getPosition();
  144. const int64 elementSize = endPos - (startPos + 4);
  145. return output.setPosition (startPos)
  146. && writeInt32 ((int32) elementSize)
  147. && output.setPosition (endPos);
  148. }
  149. private:
  150. MemoryOutputStream output;
  151. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCOutputStream)
  152. };
  153. } // namespace
  154. //==============================================================================
  155. struct OSCSender::Pimpl
  156. {
  157. Pimpl() noexcept {}
  158. ~Pimpl() noexcept { disconnect(); }
  159. //==============================================================================
  160. bool connect (const String& newTargetHost, int newTargetPort)
  161. {
  162. if (! disconnect())
  163. return false;
  164. socket = new DatagramSocket (true);
  165. targetHostName = newTargetHost;
  166. targetPortNumber = newTargetPort;
  167. if (socket->bindToPort (0)) // 0 = use any local port assigned by the OS.
  168. return true;
  169. socket = nullptr;
  170. return false;
  171. }
  172. bool disconnect()
  173. {
  174. socket = nullptr;
  175. return true;
  176. }
  177. //==============================================================================
  178. bool send (const OSCMessage& message, const String& hostName, int portNumber)
  179. {
  180. OSCOutputStream outStream;
  181. return outStream.writeMessage (message)
  182. && sendOutputStream (outStream, hostName, portNumber);
  183. }
  184. bool send (const OSCBundle& bundle, const String& hostName, int portNumber)
  185. {
  186. OSCOutputStream outStream;
  187. return outStream.writeBundle (bundle)
  188. && sendOutputStream (outStream, hostName, portNumber);
  189. }
  190. bool send (const OSCMessage& message) { return send (message, targetHostName, targetPortNumber); }
  191. bool send (const OSCBundle& bundle) { return send (bundle, targetHostName, targetPortNumber); }
  192. private:
  193. //==============================================================================
  194. bool sendOutputStream (OSCOutputStream& outStream, const String& hostName, int portNumber)
  195. {
  196. if (socket != nullptr)
  197. {
  198. const int streamSize = (int) outStream.getDataSize();
  199. const int bytesWritten = socket->write (hostName, portNumber,
  200. outStream.getData(), streamSize);
  201. return bytesWritten == streamSize;
  202. }
  203. // if you hit this, you tried to send some OSC data without being
  204. // connected to a port! You should call OSCSender::connect() first.
  205. jassertfalse;
  206. return false;
  207. }
  208. //==============================================================================
  209. ScopedPointer<DatagramSocket> socket;
  210. String targetHostName;
  211. int targetPortNumber = 0;
  212. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  213. };
  214. //==============================================================================
  215. OSCSender::OSCSender() : pimpl (new Pimpl())
  216. {
  217. }
  218. OSCSender::~OSCSender()
  219. {
  220. pimpl->disconnect();
  221. pimpl = nullptr;
  222. }
  223. //==============================================================================
  224. bool OSCSender::connect (const String& targetHostName, int targetPortNumber)
  225. {
  226. return pimpl->connect (targetHostName, targetPortNumber);
  227. }
  228. bool OSCSender::disconnect()
  229. {
  230. return pimpl->disconnect();
  231. }
  232. //==============================================================================
  233. bool OSCSender::send (const OSCMessage& message) { return pimpl->send (message); }
  234. bool OSCSender::send (const OSCBundle& bundle) { return pimpl->send (bundle); }
  235. bool OSCSender::sendToIPAddress (const String& host, int port, const OSCMessage& message) { return pimpl->send (message, host, port); }
  236. bool OSCSender::sendToIPAddress (const String& host, int port, const OSCBundle& bundle) { return pimpl->send (bundle, host, port); }
  237. //==============================================================================
  238. //==============================================================================
  239. #if JUCE_UNIT_TESTS
  240. class OSCBinaryWriterTests : public UnitTest
  241. {
  242. public:
  243. OSCBinaryWriterTests() : UnitTest ("OSCBinaryWriter class", "OSC") {}
  244. void runTest()
  245. {
  246. beginTest ("writing OSC addresses");
  247. {
  248. OSCOutputStream outStream;
  249. const char check[16] = { '/', 't', 'e', 's', 't', '/', 'f', 'a', 'd', 'e', 'r', '7', '\0', '\0', '\0', '\0' };
  250. OSCAddress address ("/test/fader7");
  251. expect (outStream.writeAddress (address));
  252. expect (outStream.getDataSize() == sizeof (check));
  253. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  254. }
  255. beginTest ("writing OSC address patterns");
  256. {
  257. OSCOutputStream outStream;
  258. const char check[20] = { '/', '*', '/', '*', 'p', 'u', 't', '/', 'f', 'a', 'd', 'e', 'r', '[', '0', '-', '9', ']', '\0', '\0' };
  259. OSCAddressPattern ap ("/*/*put/fader[0-9]");
  260. expect (outStream.writeAddressPattern (ap));
  261. expect (outStream.getDataSize() == sizeof (check));
  262. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  263. }
  264. beginTest ("writing OSC time tags");
  265. {
  266. OSCOutputStream outStream;
  267. const char check[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
  268. OSCTimeTag tag;
  269. expect (outStream.writeTimeTag (tag));
  270. expect (outStream.getDataSize() == 8);
  271. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  272. }
  273. beginTest ("writing OSC type tag strings");
  274. {
  275. {
  276. OSCOutputStream outStream;
  277. OSCTypeList list;
  278. const char check[4] = { ',', '\0', '\0', '\0' };
  279. expect (outStream.writeTypeTagString (list));
  280. expect (outStream.getDataSize() == 4);
  281. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  282. }
  283. {
  284. OSCOutputStream outStream;
  285. OSCTypeList list;
  286. list.add (OSCTypes::int32);
  287. list.add (OSCTypes::float32);
  288. const char check[4] = { ',', 'i', 'f', '\0' };
  289. expect (outStream.writeTypeTagString (list));
  290. expect (outStream.getDataSize() == sizeof (check));
  291. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  292. }
  293. {
  294. OSCOutputStream outStream;
  295. OSCTypeList list;
  296. list.add (OSCTypes::blob);
  297. list.add (OSCTypes::blob);
  298. list.add (OSCTypes::string);
  299. const char check[8] = { ',', 'b', 'b', 's', '\0', '\0', '\0', '\0' };
  300. expect (outStream.writeTypeTagString (list));
  301. expect (outStream.getDataSize() == sizeof (check));
  302. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  303. }
  304. }
  305. beginTest ("writing OSC arguments");
  306. {
  307. // test data:
  308. int testInt = -2015;
  309. const uint8 testIntRepresentation[] = { 0xFF, 0xFF, 0xF8, 0x21 }; // big endian two's complement
  310. float testFloat = 345.6125f;
  311. const uint8 testFloatRepresentation[] = { 0x43, 0xAC, 0xCE, 0x66 }; // big endian IEEE 754
  312. String testString = "Hello, World!";
  313. const char testStringRepresentation[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0' }; // padded to size % 4 == 0
  314. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  315. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  316. const uint8 testBlobRepresentation[] = { 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00 }; // padded to size % 4 == 0
  317. // write:
  318. {
  319. // int32:
  320. OSCArgument arg (testInt);
  321. OSCOutputStream outStream;
  322. expect (outStream.writeArgument (arg));
  323. expect (outStream.getDataSize() == 4);
  324. expect (std::memcmp (outStream.getData(), testIntRepresentation, sizeof (testIntRepresentation)) == 0);
  325. }
  326. {
  327. // float32:
  328. OSCArgument arg (testFloat);
  329. OSCOutputStream outStream;
  330. expect (outStream.writeArgument (arg));
  331. expect (outStream.getDataSize() == 4);
  332. expect (std::memcmp (outStream.getData(), testFloatRepresentation, sizeof (testFloatRepresentation)) == 0);
  333. }
  334. {
  335. // string:
  336. expect (testString.length() % 4 != 0); // check whether we actually cover padding
  337. expect (sizeof (testStringRepresentation) % 4 == 0);
  338. OSCArgument arg (testString);
  339. OSCOutputStream outStream;
  340. expect (outStream.writeArgument (arg));
  341. expect (outStream.getDataSize() == sizeof (testStringRepresentation));
  342. expect (std::memcmp (outStream.getData(), testStringRepresentation, sizeof (testStringRepresentation)) == 0);
  343. }
  344. {
  345. // blob:
  346. expect (testBlob.getSize() % 4 != 0); // check whether we actually cover padding
  347. expect (sizeof (testBlobRepresentation) % 4 == 0);
  348. OSCArgument arg (testBlob);
  349. OSCOutputStream outStream;
  350. expect (outStream.writeArgument (arg));
  351. expect (outStream.getDataSize() == sizeof (testBlobRepresentation));
  352. expect (std::memcmp (outStream.getData(), testBlobRepresentation, sizeof (testBlobRepresentation)) == 0);
  353. }
  354. }
  355. beginTest ("Writing strings with correct padding");
  356. {
  357. // the only OSC-specific thing to check is the correct number of padding zeros
  358. {
  359. OSCArgument with15Chars ("123456789012345");
  360. OSCOutputStream outStream;
  361. expect (outStream.writeArgument (with15Chars));
  362. expect (outStream.getDataSize() == 16);
  363. }
  364. {
  365. OSCArgument with16Chars ("1234567890123456");
  366. OSCOutputStream outStream;
  367. expect (outStream.writeArgument (with16Chars));
  368. expect (outStream.getDataSize() == 20);
  369. }
  370. {
  371. OSCArgument with17Chars ("12345678901234567");
  372. OSCOutputStream outStream;
  373. expect (outStream.writeArgument (with17Chars));
  374. expect (outStream.getDataSize() == 20);
  375. }
  376. {
  377. OSCArgument with18Chars ("123456789012345678");
  378. OSCOutputStream outStream;
  379. expect (outStream.writeArgument (with18Chars));
  380. expect (outStream.getDataSize() == 20);
  381. }
  382. {
  383. OSCArgument with19Chars ("1234567890123456789");
  384. OSCOutputStream outStream;
  385. expect (outStream.writeArgument (with19Chars));
  386. expect (outStream.getDataSize() == 20);
  387. }
  388. {
  389. OSCArgument with20Chars ("12345678901234567890");
  390. OSCOutputStream outStream;
  391. expect (outStream.writeArgument (with20Chars));
  392. expect (outStream.getDataSize() == 24);
  393. }
  394. }
  395. beginTest ("Writing blobs with correct padding");
  396. {
  397. const char buffer[20] = {};
  398. {
  399. OSCArgument with15Bytes (MemoryBlock (buffer, 15));
  400. OSCOutputStream outStream;
  401. expect (outStream.writeArgument (with15Bytes));
  402. expect (outStream.getDataSize() == 20);
  403. }
  404. {
  405. OSCArgument with16Bytes (MemoryBlock (buffer, 16));
  406. OSCOutputStream outStream;
  407. expect (outStream.writeArgument (with16Bytes));
  408. expect (outStream.getDataSize() == 20);
  409. }
  410. {
  411. OSCArgument with17Bytes (MemoryBlock (buffer, 17));
  412. OSCOutputStream outStream;
  413. expect (outStream.writeArgument (with17Bytes));
  414. expect (outStream.getDataSize() == 24);
  415. }
  416. {
  417. OSCArgument with18Bytes (MemoryBlock (buffer, 18));
  418. OSCOutputStream outStream;
  419. expect (outStream.writeArgument (with18Bytes));
  420. expect (outStream.getDataSize() == 24);
  421. }
  422. {
  423. OSCArgument with19Bytes (MemoryBlock (buffer, 19));
  424. OSCOutputStream outStream;
  425. expect (outStream.writeArgument (with19Bytes));
  426. expect (outStream.getDataSize() == 24);
  427. }
  428. {
  429. OSCArgument with20Bytes (MemoryBlock (buffer, 20));
  430. OSCOutputStream outStream;
  431. expect (outStream.writeArgument (with20Bytes));
  432. expect (outStream.getDataSize() == 24);
  433. }
  434. }
  435. beginTest ("Writing OSC messages.");
  436. {
  437. {
  438. int32 testInt = -2015;
  439. float testFloat = 345.6125f;
  440. String testString = "Hello, World!";
  441. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  442. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  443. uint8 check[52] = { '/', 't', 'e', 's', 't', '\0', '\0', '\0',
  444. ',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
  445. 0xFF, 0xFF, 0xF8, 0x21,
  446. 0x43, 0xAC, 0xCE, 0x66,
  447. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
  448. 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00
  449. };
  450. OSCOutputStream outStream;
  451. OSCMessage msg ("/test");
  452. msg.addInt32 (testInt);
  453. msg.addFloat32 (testFloat);
  454. msg.addString (testString);
  455. msg.addBlob (testBlob);
  456. expect (outStream.writeMessage (msg));
  457. expect (outStream.getDataSize() == sizeof (check));
  458. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  459. }
  460. }
  461. beginTest ("Writing OSC bundle.");
  462. {
  463. {
  464. int32 testInt = -2015;
  465. float testFloat = 345.6125f;
  466. String testString = "Hello, World!";
  467. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  468. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  469. uint8 check[] = {
  470. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  471. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  472. 0x00, 0x00, 0x00, 0x34,
  473. '/', 't', 'e', 's', 't', '/', '1', '\0',
  474. ',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
  475. 0xFF, 0xFF, 0xF8, 0x21,
  476. 0x43, 0xAC, 0xCE, 0x66,
  477. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
  478. 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00,
  479. 0x00, 0x00, 0x00, 0x0C,
  480. '/', 't', 'e', 's', 't', '/', '2', '\0',
  481. ',', '\0', '\0', '\0',
  482. 0x00, 0x00, 0x00, 0x10,
  483. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  484. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  485. };
  486. OSCOutputStream outStream;
  487. OSCBundle bundle;
  488. OSCMessage msg1 ("/test/1");
  489. msg1.addInt32 (testInt);
  490. msg1.addFloat32 (testFloat);
  491. msg1.addString (testString);
  492. msg1.addBlob (testBlob);
  493. bundle.addElement (msg1);
  494. OSCMessage msg2 ("/test/2");
  495. bundle.addElement (msg2);
  496. OSCBundle subBundle;
  497. bundle.addElement (subBundle);
  498. expect (outStream.writeBundle (bundle));
  499. expect (outStream.getDataSize() == sizeof (check));
  500. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  501. }
  502. }
  503. }
  504. };
  505. static OSCBinaryWriterTests OSCBinaryWriterUnitTests;
  506. //==============================================================================
  507. class OSCRoundTripTests : public UnitTest
  508. {
  509. public:
  510. OSCRoundTripTests() : UnitTest ("OSCRoundTripTests class", "OSC") {}
  511. void runTest()
  512. {
  513. beginTest ("Empty OSC message");
  514. {
  515. OSCMessage outMessage ("/test/empty");
  516. OSCOutputStream output;
  517. output.writeMessage (outMessage);
  518. OSCInputStream input (output.getData(), output.getDataSize());
  519. OSCMessage inMessage = input.readMessage();
  520. expectEquals (inMessage.size(), 0);
  521. }
  522. beginTest ("OSC message with single argument");
  523. {
  524. OSCMessage outMessage ("/test/one_arg", 42);
  525. OSCOutputStream output;
  526. output.writeMessage (outMessage);
  527. OSCInputStream input (output.getData(), output.getDataSize());
  528. OSCMessage inMessage = input.readMessage();
  529. expectEquals (inMessage.size(), 1);
  530. expectEquals (inMessage[0].getInt32(), 42);
  531. }
  532. beginTest ("OSC message with multiple arguments");
  533. {
  534. OSCMessage outMessage ("/test/four_args", 42, 0.5f, String ("foo"), String ("bar"));
  535. OSCOutputStream output;
  536. output.writeMessage (outMessage);
  537. OSCInputStream input (output.getData(), output.getDataSize());
  538. OSCMessage inMessage = input.readMessage();
  539. expectEquals (inMessage.size(), 4);
  540. expectEquals (inMessage[0].getInt32(), 42);
  541. expectEquals (inMessage[1].getFloat32(), 0.5f);
  542. expectEquals (inMessage[2].getString(), String ("foo"));
  543. expectEquals (inMessage[3].getString(), String ("bar"));
  544. }
  545. beginTest ("Empty OSC bundle");
  546. {
  547. OSCBundle outBundle;
  548. OSCOutputStream output;
  549. output.writeBundle (outBundle);
  550. OSCInputStream input (output.getData(), output.getDataSize());
  551. OSCBundle inBundle = input.readBundle();
  552. expectEquals (inBundle.size(), 0);
  553. }
  554. beginTest ("OSC bundle with single message");
  555. {
  556. OSCMessage outMessage ("/test/one_arg", 42);
  557. OSCBundle outBundle;
  558. outBundle.addElement (outMessage);
  559. OSCOutputStream output;
  560. output.writeBundle (outBundle);
  561. OSCInputStream input (output.getData(), output.getDataSize());
  562. OSCBundle inBundle = input.readBundle();
  563. expectEquals (inBundle.size(), 1);
  564. OSCMessage inMessage = inBundle[0].getMessage();
  565. expectEquals (inMessage.getAddressPattern().toString(), String ("/test/one_arg"));
  566. expectEquals (inMessage.size(), 1);
  567. expectEquals (inMessage[0].getInt32(), 42);
  568. }
  569. beginTest ("OSC bundle with multiple messages");
  570. {
  571. OSCMessage outMessage1 ("/test/empty");
  572. OSCMessage outMessage2 ("/test/one_arg", 42);
  573. OSCMessage outMessage3 ("/test/four_args", 42, 0.5f, String ("foo"), String ("bar"));
  574. OSCBundle outBundle;
  575. outBundle.addElement (outMessage1);
  576. outBundle.addElement (outMessage2);
  577. outBundle.addElement (outMessage3);
  578. OSCOutputStream output;
  579. output.writeBundle (outBundle);
  580. OSCInputStream input (output.getData(), output.getDataSize());
  581. OSCBundle inBundle = input.readBundle();
  582. expectEquals (inBundle.size(), 3);
  583. {
  584. OSCMessage inMessage = inBundle[0].getMessage();
  585. expectEquals (inMessage.getAddressPattern().toString(), String ("/test/empty"));
  586. expectEquals (inMessage.size(), 0);
  587. }
  588. {
  589. OSCMessage inMessage = inBundle[1].getMessage();
  590. expectEquals (inMessage.getAddressPattern().toString(), String ("/test/one_arg"));
  591. expectEquals (inMessage.size(), 1);
  592. expectEquals (inMessage[0].getInt32(), 42);
  593. }
  594. {
  595. OSCMessage inMessage = inBundle[2].getMessage();
  596. expectEquals (inMessage.getAddressPattern().toString(), String ("/test/four_args"));
  597. expectEquals (inMessage.size(), 4);
  598. expectEquals (inMessage[0].getInt32(), 42);
  599. expectEquals (inMessage[1].getFloat32(), 0.5f);
  600. expectEquals (inMessage[2].getString(), String ("foo"));
  601. expectEquals (inMessage[3].getString(), String ("bar"));
  602. }
  603. }
  604. beginTest ("OSC bundle containing another bundle");
  605. {
  606. OSCBundle outBundleNested;
  607. outBundleNested.addElement (OSCMessage ("/test/one_arg", 42));
  608. OSCBundle outBundle;
  609. outBundle.addElement (outBundleNested);
  610. OSCOutputStream output;
  611. output.writeBundle (outBundle);
  612. OSCInputStream input (output.getData(), output.getDataSize());
  613. OSCBundle inBundle = input.readBundle();
  614. expectEquals (inBundle.size(), 1);
  615. expect (inBundle[0].isBundle());
  616. OSCBundle inBundleNested = inBundle[0].getBundle();
  617. expectEquals (inBundleNested.size(), 1);
  618. expect (inBundleNested[0].isMessage());
  619. OSCMessage msg = inBundleNested[0].getMessage();
  620. expectEquals (msg.getAddressPattern().toString(), String ("/test/one_arg"));
  621. expectEquals (msg.size(), 1);
  622. expectEquals (msg[0].getInt32(), 42);
  623. }
  624. beginTest ("OSC bundle containing multiple other bundles");
  625. {
  626. OSCBundle outBundleNested1;
  627. outBundleNested1.addElement (OSCMessage ("/test/empty"));
  628. OSCBundle outBundleNested2;
  629. outBundleNested2.addElement (OSCMessage ("/test/one_arg", 42));
  630. OSCBundle outBundle;
  631. outBundle.addElement (outBundleNested1);
  632. outBundle.addElement (outBundleNested2);
  633. OSCOutputStream output;
  634. output.writeBundle (outBundle);
  635. OSCInputStream input (output.getData(), output.getDataSize());
  636. OSCBundle inBundle = input.readBundle();
  637. expectEquals (inBundle.size(), 2);
  638. {
  639. expect (inBundle[0].isBundle());
  640. OSCBundle inBundleNested = inBundle[0].getBundle();
  641. expectEquals (inBundleNested.size(), 1);
  642. expect (inBundleNested[0].isMessage());
  643. OSCMessage msg = inBundleNested[0].getMessage();
  644. expectEquals (msg.getAddressPattern().toString(), String ("/test/empty"));
  645. expectEquals (msg.size(), 0);
  646. }
  647. {
  648. expect (inBundle[1].isBundle());
  649. OSCBundle inBundleNested = inBundle[1].getBundle();
  650. expectEquals (inBundleNested.size(), 1);
  651. expect (inBundleNested[0].isMessage());
  652. OSCMessage msg = inBundleNested[0].getMessage();
  653. expectEquals (msg.getAddressPattern().toString(), String ("/test/one_arg"));
  654. expectEquals (msg.size(), 1);
  655. expectEquals (msg[0].getInt32(), 42);
  656. }
  657. }
  658. }
  659. };
  660. static OSCRoundTripTests OSCRoundTripUnitTests;
  661. //==============================================================================
  662. #endif // JUCE_UNIT_TESTS
  663. } // namespace juce