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.

877 lines
31KB

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