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.

843 lines
31KB

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