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.

847 lines
30KB

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