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.

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