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.

629 lines
22KB

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