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.

868 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.setOwned (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.reset();
  170. return false;
  171. }
  172. bool connectToSocket (DatagramSocket& newSocket, const String& newTargetHost, int newTargetPort)
  173. {
  174. if (! disconnect())
  175. return false;
  176. socket.setNonOwned (&newSocket);
  177. targetHostName = newTargetHost;
  178. targetPortNumber = newTargetPort;
  179. return true;
  180. }
  181. bool disconnect()
  182. {
  183. socket.reset();
  184. return true;
  185. }
  186. //==============================================================================
  187. bool send (const OSCMessage& message, const String& hostName, int portNumber)
  188. {
  189. OSCOutputStream outStream;
  190. return outStream.writeMessage (message)
  191. && sendOutputStream (outStream, hostName, portNumber);
  192. }
  193. bool send (const OSCBundle& bundle, const String& hostName, int portNumber)
  194. {
  195. OSCOutputStream outStream;
  196. return outStream.writeBundle (bundle)
  197. && sendOutputStream (outStream, hostName, portNumber);
  198. }
  199. bool send (const OSCMessage& message) { return send (message, targetHostName, targetPortNumber); }
  200. bool send (const OSCBundle& bundle) { return send (bundle, targetHostName, targetPortNumber); }
  201. private:
  202. //==============================================================================
  203. bool sendOutputStream (OSCOutputStream& outStream, const String& hostName, int portNumber)
  204. {
  205. if (socket != nullptr)
  206. {
  207. const int streamSize = (int) outStream.getDataSize();
  208. const int bytesWritten = socket->write (hostName, portNumber,
  209. outStream.getData(), streamSize);
  210. return bytesWritten == streamSize;
  211. }
  212. // if you hit this, you tried to send some OSC data without being
  213. // connected to a port! You should call OSCSender::connect() first.
  214. jassertfalse;
  215. return false;
  216. }
  217. //==============================================================================
  218. OptionalScopedPointer<DatagramSocket> socket;
  219. String targetHostName;
  220. int targetPortNumber = 0;
  221. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  222. };
  223. //==============================================================================
  224. OSCSender::OSCSender() : pimpl (new Pimpl())
  225. {
  226. }
  227. OSCSender::~OSCSender()
  228. {
  229. pimpl->disconnect();
  230. pimpl.reset();
  231. }
  232. //==============================================================================
  233. bool OSCSender::connect (const String& targetHostName, int targetPortNumber)
  234. {
  235. return pimpl->connect (targetHostName, targetPortNumber);
  236. }
  237. bool OSCSender::connectToSocket (DatagramSocket& socket, const String& targetHostName, int targetPortNumber)
  238. {
  239. return pimpl->connectToSocket (socket, targetHostName, targetPortNumber);
  240. }
  241. bool OSCSender::disconnect()
  242. {
  243. return pimpl->disconnect();
  244. }
  245. //==============================================================================
  246. bool OSCSender::send (const OSCMessage& message) { return pimpl->send (message); }
  247. bool OSCSender::send (const OSCBundle& bundle) { return pimpl->send (bundle); }
  248. bool OSCSender::sendToIPAddress (const String& host, int port, const OSCMessage& message) { return pimpl->send (message, host, port); }
  249. bool OSCSender::sendToIPAddress (const String& host, int port, const OSCBundle& bundle) { return pimpl->send (bundle, host, port); }
  250. //==============================================================================
  251. //==============================================================================
  252. #if JUCE_UNIT_TESTS
  253. class OSCBinaryWriterTests : public UnitTest
  254. {
  255. public:
  256. OSCBinaryWriterTests() : UnitTest ("OSCBinaryWriter class", "OSC") {}
  257. void runTest()
  258. {
  259. beginTest ("writing OSC addresses");
  260. {
  261. OSCOutputStream outStream;
  262. const char check[16] = { '/', 't', 'e', 's', 't', '/', 'f', 'a', 'd', 'e', 'r', '7', '\0', '\0', '\0', '\0' };
  263. OSCAddress address ("/test/fader7");
  264. expect (outStream.writeAddress (address));
  265. expect (outStream.getDataSize() == sizeof (check));
  266. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  267. }
  268. beginTest ("writing OSC address patterns");
  269. {
  270. OSCOutputStream outStream;
  271. const char check[20] = { '/', '*', '/', '*', 'p', 'u', 't', '/', 'f', 'a', 'd', 'e', 'r', '[', '0', '-', '9', ']', '\0', '\0' };
  272. OSCAddressPattern ap ("/*/*put/fader[0-9]");
  273. expect (outStream.writeAddressPattern (ap));
  274. expect (outStream.getDataSize() == sizeof (check));
  275. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  276. }
  277. beginTest ("writing OSC time tags");
  278. {
  279. OSCOutputStream outStream;
  280. const char check[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
  281. OSCTimeTag tag;
  282. expect (outStream.writeTimeTag (tag));
  283. expect (outStream.getDataSize() == 8);
  284. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  285. }
  286. beginTest ("writing OSC type tag strings");
  287. {
  288. {
  289. OSCOutputStream outStream;
  290. OSCTypeList list;
  291. const char check[4] = { ',', '\0', '\0', '\0' };
  292. expect (outStream.writeTypeTagString (list));
  293. expect (outStream.getDataSize() == 4);
  294. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  295. }
  296. {
  297. OSCOutputStream outStream;
  298. OSCTypeList list;
  299. list.add (OSCTypes::int32);
  300. list.add (OSCTypes::float32);
  301. const char check[4] = { ',', 'i', 'f', '\0' };
  302. expect (outStream.writeTypeTagString (list));
  303. expect (outStream.getDataSize() == sizeof (check));
  304. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  305. }
  306. {
  307. OSCOutputStream outStream;
  308. OSCTypeList list;
  309. list.add (OSCTypes::blob);
  310. list.add (OSCTypes::blob);
  311. list.add (OSCTypes::string);
  312. const char check[8] = { ',', 'b', 'b', 's', '\0', '\0', '\0', '\0' };
  313. expect (outStream.writeTypeTagString (list));
  314. expect (outStream.getDataSize() == sizeof (check));
  315. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  316. }
  317. }
  318. beginTest ("writing OSC arguments");
  319. {
  320. // test data:
  321. int testInt = -2015;
  322. const uint8 testIntRepresentation[] = { 0xFF, 0xFF, 0xF8, 0x21 }; // big endian two's complement
  323. float testFloat = 345.6125f;
  324. const uint8 testFloatRepresentation[] = { 0x43, 0xAC, 0xCE, 0x66 }; // big endian IEEE 754
  325. String testString = "Hello, World!";
  326. const char testStringRepresentation[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0' }; // padded to size % 4 == 0
  327. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  328. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  329. const uint8 testBlobRepresentation[] = { 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00 }; // padded to size % 4 == 0
  330. // write:
  331. {
  332. // int32:
  333. OSCArgument arg (testInt);
  334. OSCOutputStream outStream;
  335. expect (outStream.writeArgument (arg));
  336. expect (outStream.getDataSize() == 4);
  337. expect (std::memcmp (outStream.getData(), testIntRepresentation, sizeof (testIntRepresentation)) == 0);
  338. }
  339. {
  340. // float32:
  341. OSCArgument arg (testFloat);
  342. OSCOutputStream outStream;
  343. expect (outStream.writeArgument (arg));
  344. expect (outStream.getDataSize() == 4);
  345. expect (std::memcmp (outStream.getData(), testFloatRepresentation, sizeof (testFloatRepresentation)) == 0);
  346. }
  347. {
  348. // string:
  349. expect (testString.length() % 4 != 0); // check whether we actually cover padding
  350. expect (sizeof (testStringRepresentation) % 4 == 0);
  351. OSCArgument arg (testString);
  352. OSCOutputStream outStream;
  353. expect (outStream.writeArgument (arg));
  354. expect (outStream.getDataSize() == sizeof (testStringRepresentation));
  355. expect (std::memcmp (outStream.getData(), testStringRepresentation, sizeof (testStringRepresentation)) == 0);
  356. }
  357. {
  358. // blob:
  359. expect (testBlob.getSize() % 4 != 0); // check whether we actually cover padding
  360. expect (sizeof (testBlobRepresentation) % 4 == 0);
  361. OSCArgument arg (testBlob);
  362. OSCOutputStream outStream;
  363. expect (outStream.writeArgument (arg));
  364. expect (outStream.getDataSize() == sizeof (testBlobRepresentation));
  365. expect (std::memcmp (outStream.getData(), testBlobRepresentation, sizeof (testBlobRepresentation)) == 0);
  366. }
  367. }
  368. beginTest ("Writing strings with correct padding");
  369. {
  370. // the only OSC-specific thing to check is the correct number of padding zeros
  371. {
  372. OSCArgument with15Chars ("123456789012345");
  373. OSCOutputStream outStream;
  374. expect (outStream.writeArgument (with15Chars));
  375. expect (outStream.getDataSize() == 16);
  376. }
  377. {
  378. OSCArgument with16Chars ("1234567890123456");
  379. OSCOutputStream outStream;
  380. expect (outStream.writeArgument (with16Chars));
  381. expect (outStream.getDataSize() == 20);
  382. }
  383. {
  384. OSCArgument with17Chars ("12345678901234567");
  385. OSCOutputStream outStream;
  386. expect (outStream.writeArgument (with17Chars));
  387. expect (outStream.getDataSize() == 20);
  388. }
  389. {
  390. OSCArgument with18Chars ("123456789012345678");
  391. OSCOutputStream outStream;
  392. expect (outStream.writeArgument (with18Chars));
  393. expect (outStream.getDataSize() == 20);
  394. }
  395. {
  396. OSCArgument with19Chars ("1234567890123456789");
  397. OSCOutputStream outStream;
  398. expect (outStream.writeArgument (with19Chars));
  399. expect (outStream.getDataSize() == 20);
  400. }
  401. {
  402. OSCArgument with20Chars ("12345678901234567890");
  403. OSCOutputStream outStream;
  404. expect (outStream.writeArgument (with20Chars));
  405. expect (outStream.getDataSize() == 24);
  406. }
  407. }
  408. beginTest ("Writing blobs with correct padding");
  409. {
  410. const char buffer[20] = {};
  411. {
  412. OSCArgument with15Bytes (MemoryBlock (buffer, 15));
  413. OSCOutputStream outStream;
  414. expect (outStream.writeArgument (with15Bytes));
  415. expect (outStream.getDataSize() == 20);
  416. }
  417. {
  418. OSCArgument with16Bytes (MemoryBlock (buffer, 16));
  419. OSCOutputStream outStream;
  420. expect (outStream.writeArgument (with16Bytes));
  421. expect (outStream.getDataSize() == 20);
  422. }
  423. {
  424. OSCArgument with17Bytes (MemoryBlock (buffer, 17));
  425. OSCOutputStream outStream;
  426. expect (outStream.writeArgument (with17Bytes));
  427. expect (outStream.getDataSize() == 24);
  428. }
  429. {
  430. OSCArgument with18Bytes (MemoryBlock (buffer, 18));
  431. OSCOutputStream outStream;
  432. expect (outStream.writeArgument (with18Bytes));
  433. expect (outStream.getDataSize() == 24);
  434. }
  435. {
  436. OSCArgument with19Bytes (MemoryBlock (buffer, 19));
  437. OSCOutputStream outStream;
  438. expect (outStream.writeArgument (with19Bytes));
  439. expect (outStream.getDataSize() == 24);
  440. }
  441. {
  442. OSCArgument with20Bytes (MemoryBlock (buffer, 20));
  443. OSCOutputStream outStream;
  444. expect (outStream.writeArgument (with20Bytes));
  445. expect (outStream.getDataSize() == 24);
  446. }
  447. }
  448. beginTest ("Writing OSC messages.");
  449. {
  450. {
  451. int32 testInt = -2015;
  452. float testFloat = 345.6125f;
  453. String testString = "Hello, World!";
  454. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  455. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  456. uint8 check[52] = { '/', 't', 'e', 's', 't', '\0', '\0', '\0',
  457. ',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
  458. 0xFF, 0xFF, 0xF8, 0x21,
  459. 0x43, 0xAC, 0xCE, 0x66,
  460. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
  461. 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00
  462. };
  463. OSCOutputStream outStream;
  464. OSCMessage msg ("/test");
  465. msg.addInt32 (testInt);
  466. msg.addFloat32 (testFloat);
  467. msg.addString (testString);
  468. msg.addBlob (testBlob);
  469. expect (outStream.writeMessage (msg));
  470. expect (outStream.getDataSize() == sizeof (check));
  471. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  472. }
  473. }
  474. beginTest ("Writing OSC bundle.");
  475. {
  476. {
  477. int32 testInt = -2015;
  478. float testFloat = 345.6125f;
  479. String testString = "Hello, World!";
  480. const uint8 testBlobData[] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  481. const MemoryBlock testBlob (testBlobData, sizeof (testBlobData));
  482. uint8 check[] = {
  483. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  484. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  485. 0x00, 0x00, 0x00, 0x34,
  486. '/', 't', 'e', 's', 't', '/', '1', '\0',
  487. ',', 'i', 'f', 's', 'b', '\0', '\0', '\0',
  488. 0xFF, 0xFF, 0xF8, 0x21,
  489. 0x43, 0xAC, 0xCE, 0x66,
  490. 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', '\0', '\0',
  491. 0x00, 0x00, 0x00, 0x05, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x00, 0x00,
  492. 0x00, 0x00, 0x00, 0x0C,
  493. '/', 't', 'e', 's', 't', '/', '2', '\0',
  494. ',', '\0', '\0', '\0',
  495. 0x00, 0x00, 0x00, 0x10,
  496. '#', 'b', 'u', 'n', 'd', 'l', 'e', '\0',
  497. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  498. };
  499. OSCOutputStream outStream;
  500. OSCBundle bundle;
  501. OSCMessage msg1 ("/test/1");
  502. msg1.addInt32 (testInt);
  503. msg1.addFloat32 (testFloat);
  504. msg1.addString (testString);
  505. msg1.addBlob (testBlob);
  506. bundle.addElement (msg1);
  507. OSCMessage msg2 ("/test/2");
  508. bundle.addElement (msg2);
  509. OSCBundle subBundle;
  510. bundle.addElement (subBundle);
  511. expect (outStream.writeBundle (bundle));
  512. expect (outStream.getDataSize() == sizeof (check));
  513. expect (std::memcmp (outStream.getData(), check, sizeof (check)) == 0);
  514. }
  515. }
  516. }
  517. };
  518. static OSCBinaryWriterTests OSCBinaryWriterUnitTests;
  519. //==============================================================================
  520. class OSCRoundTripTests : public UnitTest
  521. {
  522. public:
  523. OSCRoundTripTests() : UnitTest ("OSCRoundTripTests class", "OSC") {}
  524. void runTest()
  525. {
  526. beginTest ("Empty OSC message");
  527. {
  528. OSCMessage outMessage ("/test/empty");
  529. OSCOutputStream output;
  530. output.writeMessage (outMessage);
  531. OSCInputStream input (output.getData(), output.getDataSize());
  532. OSCMessage inMessage = input.readMessage();
  533. expectEquals (inMessage.size(), 0);
  534. }
  535. beginTest ("OSC message with single argument");
  536. {
  537. OSCMessage outMessage ("/test/one_arg", 42);
  538. OSCOutputStream output;
  539. output.writeMessage (outMessage);
  540. OSCInputStream input (output.getData(), output.getDataSize());
  541. OSCMessage inMessage = input.readMessage();
  542. expectEquals (inMessage.size(), 1);
  543. expectEquals (inMessage[0].getInt32(), 42);
  544. }
  545. beginTest ("OSC message with multiple arguments");
  546. {
  547. OSCMessage outMessage ("/test/four_args", 42, 0.5f, String ("foo"), String ("bar"));
  548. OSCOutputStream output;
  549. output.writeMessage (outMessage);
  550. OSCInputStream input (output.getData(), output.getDataSize());
  551. OSCMessage inMessage = input.readMessage();
  552. expectEquals (inMessage.size(), 4);
  553. expectEquals (inMessage[0].getInt32(), 42);
  554. expectEquals (inMessage[1].getFloat32(), 0.5f);
  555. expectEquals (inMessage[2].getString(), String ("foo"));
  556. expectEquals (inMessage[3].getString(), String ("bar"));
  557. }
  558. beginTest ("Empty OSC bundle");
  559. {
  560. OSCBundle outBundle;
  561. OSCOutputStream output;
  562. output.writeBundle (outBundle);
  563. OSCInputStream input (output.getData(), output.getDataSize());
  564. OSCBundle inBundle = input.readBundle();
  565. expectEquals (inBundle.size(), 0);
  566. }
  567. beginTest ("OSC bundle with single message");
  568. {
  569. OSCMessage outMessage ("/test/one_arg", 42);
  570. OSCBundle outBundle;
  571. outBundle.addElement (outMessage);
  572. OSCOutputStream output;
  573. output.writeBundle (outBundle);
  574. OSCInputStream input (output.getData(), output.getDataSize());
  575. OSCBundle inBundle = input.readBundle();
  576. expectEquals (inBundle.size(), 1);
  577. OSCMessage inMessage = inBundle[0].getMessage();
  578. expectEquals (inMessage.getAddressPattern().toString(), String ("/test/one_arg"));
  579. expectEquals (inMessage.size(), 1);
  580. expectEquals (inMessage[0].getInt32(), 42);
  581. }
  582. beginTest ("OSC bundle with multiple messages");
  583. {
  584. OSCMessage outMessage1 ("/test/empty");
  585. OSCMessage outMessage2 ("/test/one_arg", 42);
  586. OSCMessage outMessage3 ("/test/four_args", 42, 0.5f, String ("foo"), String ("bar"));
  587. OSCBundle outBundle;
  588. outBundle.addElement (outMessage1);
  589. outBundle.addElement (outMessage2);
  590. outBundle.addElement (outMessage3);
  591. OSCOutputStream output;
  592. output.writeBundle (outBundle);
  593. OSCInputStream input (output.getData(), output.getDataSize());
  594. OSCBundle inBundle = input.readBundle();
  595. expectEquals (inBundle.size(), 3);
  596. {
  597. OSCMessage inMessage = inBundle[0].getMessage();
  598. expectEquals (inMessage.getAddressPattern().toString(), String ("/test/empty"));
  599. expectEquals (inMessage.size(), 0);
  600. }
  601. {
  602. OSCMessage inMessage = inBundle[1].getMessage();
  603. expectEquals (inMessage.getAddressPattern().toString(), String ("/test/one_arg"));
  604. expectEquals (inMessage.size(), 1);
  605. expectEquals (inMessage[0].getInt32(), 42);
  606. }
  607. {
  608. OSCMessage inMessage = inBundle[2].getMessage();
  609. expectEquals (inMessage.getAddressPattern().toString(), String ("/test/four_args"));
  610. expectEquals (inMessage.size(), 4);
  611. expectEquals (inMessage[0].getInt32(), 42);
  612. expectEquals (inMessage[1].getFloat32(), 0.5f);
  613. expectEquals (inMessage[2].getString(), String ("foo"));
  614. expectEquals (inMessage[3].getString(), String ("bar"));
  615. }
  616. }
  617. beginTest ("OSC bundle containing another bundle");
  618. {
  619. OSCBundle outBundleNested;
  620. outBundleNested.addElement (OSCMessage ("/test/one_arg", 42));
  621. OSCBundle outBundle;
  622. outBundle.addElement (outBundleNested);
  623. OSCOutputStream output;
  624. output.writeBundle (outBundle);
  625. OSCInputStream input (output.getData(), output.getDataSize());
  626. OSCBundle inBundle = input.readBundle();
  627. expectEquals (inBundle.size(), 1);
  628. expect (inBundle[0].isBundle());
  629. OSCBundle inBundleNested = inBundle[0].getBundle();
  630. expectEquals (inBundleNested.size(), 1);
  631. expect (inBundleNested[0].isMessage());
  632. OSCMessage msg = inBundleNested[0].getMessage();
  633. expectEquals (msg.getAddressPattern().toString(), String ("/test/one_arg"));
  634. expectEquals (msg.size(), 1);
  635. expectEquals (msg[0].getInt32(), 42);
  636. }
  637. beginTest ("OSC bundle containing multiple other bundles");
  638. {
  639. OSCBundle outBundleNested1;
  640. outBundleNested1.addElement (OSCMessage ("/test/empty"));
  641. OSCBundle outBundleNested2;
  642. outBundleNested2.addElement (OSCMessage ("/test/one_arg", 42));
  643. OSCBundle outBundle;
  644. outBundle.addElement (outBundleNested1);
  645. outBundle.addElement (outBundleNested2);
  646. OSCOutputStream output;
  647. output.writeBundle (outBundle);
  648. OSCInputStream input (output.getData(), output.getDataSize());
  649. OSCBundle inBundle = input.readBundle();
  650. expectEquals (inBundle.size(), 2);
  651. {
  652. expect (inBundle[0].isBundle());
  653. OSCBundle inBundleNested = inBundle[0].getBundle();
  654. expectEquals (inBundleNested.size(), 1);
  655. expect (inBundleNested[0].isMessage());
  656. OSCMessage msg = inBundleNested[0].getMessage();
  657. expectEquals (msg.getAddressPattern().toString(), String ("/test/empty"));
  658. expectEquals (msg.size(), 0);
  659. }
  660. {
  661. expect (inBundle[1].isBundle());
  662. OSCBundle inBundleNested = inBundle[1].getBundle();
  663. expectEquals (inBundleNested.size(), 1);
  664. expect (inBundleNested[0].isMessage());
  665. OSCMessage msg = inBundleNested[0].getMessage();
  666. expectEquals (msg.getAddressPattern().toString(), String ("/test/one_arg"));
  667. expectEquals (msg.size(), 1);
  668. expectEquals (msg[0].getInt32(), 42);
  669. }
  670. }
  671. }
  672. };
  673. static OSCRoundTripTests OSCRoundTripUnitTests;
  674. //==============================================================================
  675. #endif // JUCE_UNIT_TESTS
  676. } // namespace juce