Audio plugin host https://kx.studio/carla
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.

594 lines
16KB

  1. /*
  2. * Carla Ring Buffer
  3. * Copyright (C) 2013-2023 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #ifndef CARLA_RING_BUFFER_HPP_INCLUDED
  18. #define CARLA_RING_BUFFER_HPP_INCLUDED
  19. #include "CarlaMathUtils.hpp"
  20. #include "CarlaMemUtils.hpp"
  21. // --------------------------------------------------------------------------------------------------------------------
  22. // Buffer structs
  23. /*
  24. head:
  25. current writing position, headmost position of the buffer.
  26. increments when writing.
  27. tail:
  28. current reading position, last used position of the buffer.
  29. increments when reading.
  30. head == tail means empty buffer
  31. wrtn:
  32. temporary position of head until a commitWrite() is called.
  33. if buffer writing fails, wrtn will be back to head position thus ignoring the last operation(s).
  34. if buffer writing succeeds, head will be set to this variable.
  35. invalidateCommit:
  36. boolean used to check if a write operation failed.
  37. this ensures we don't get incomplete writes.
  38. */
  39. struct HeapBuffer {
  40. uint32_t size;
  41. uint32_t head, tail, wrtn;
  42. bool invalidateCommit;
  43. uint8_t* buf;
  44. void copyDataFrom(const HeapBuffer& rb) noexcept
  45. {
  46. CARLA_SAFE_ASSERT_RETURN(size == rb.size,);
  47. head = rb.head;
  48. tail = rb.tail;
  49. wrtn = rb.wrtn;
  50. invalidateCommit = rb.invalidateCommit;
  51. std::memcpy(buf, rb.buf, size);
  52. }
  53. };
  54. struct SmallStackBuffer {
  55. static constexpr const uint32_t size = 4096;
  56. uint32_t head, tail, wrtn;
  57. bool invalidateCommit;
  58. uint8_t buf[size];
  59. };
  60. struct BigStackBuffer {
  61. static constexpr const uint32_t size = 16384;
  62. uint32_t head, tail, wrtn;
  63. bool invalidateCommit;
  64. uint8_t buf[size];
  65. };
  66. struct HugeStackBuffer {
  67. static constexpr const uint32_t size = 65536;
  68. uint32_t head, tail, wrtn;
  69. bool invalidateCommit;
  70. uint8_t buf[size];
  71. };
  72. // --------------------------------------------------------------------------------------------------------------------
  73. // CarlaRingBufferControl templated class
  74. template <class BufferStruct>
  75. class CarlaRingBufferControl
  76. {
  77. public:
  78. CarlaRingBufferControl() noexcept
  79. : fBuffer(nullptr),
  80. fErrorReading(false),
  81. fErrorWriting(false) {}
  82. virtual ~CarlaRingBufferControl() noexcept {}
  83. // ----------------------------------------------------------------------------------------------------------------
  84. void clearData() noexcept
  85. {
  86. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  87. fBuffer->head = fBuffer->tail = fBuffer->wrtn = 0;
  88. fBuffer->invalidateCommit = false;
  89. carla_zeroBytes(fBuffer->buf, fBuffer->size);
  90. fErrorReading = fErrorWriting = false;
  91. }
  92. void flush() noexcept
  93. {
  94. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  95. fBuffer->head = fBuffer->tail = fBuffer->wrtn = 0;
  96. fBuffer->invalidateCommit = false;
  97. fErrorWriting = false;
  98. }
  99. // ----------------------------------------------------------------------------------------------------------------
  100. bool commitWrite() noexcept
  101. {
  102. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  103. if (fBuffer->invalidateCommit)
  104. {
  105. fBuffer->wrtn = fBuffer->head;
  106. fBuffer->invalidateCommit = false;
  107. return false;
  108. }
  109. // nothing to commit?
  110. CARLA_SAFE_ASSERT_RETURN(fBuffer->head != fBuffer->wrtn, false);
  111. // all ok
  112. fBuffer->head = fBuffer->wrtn;
  113. fErrorWriting = false;
  114. return true;
  115. }
  116. bool isDataAvailableForReading() const noexcept;
  117. bool isEmpty() const noexcept
  118. {
  119. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  120. return fBuffer->buf == nullptr || fBuffer->head == fBuffer->tail;
  121. }
  122. uint32_t getSize() const noexcept
  123. {
  124. return fBuffer != nullptr ? fBuffer->size : 0;
  125. }
  126. uint32_t getReadableDataSize() const noexcept
  127. {
  128. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0);
  129. const uint32_t wrap = fBuffer->head >= fBuffer->tail ? 0 : fBuffer->size;
  130. return wrap + fBuffer->head - fBuffer->tail;
  131. }
  132. uint32_t getWritableDataSize() const noexcept
  133. {
  134. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0);
  135. const uint32_t wrap = fBuffer->tail > fBuffer->wrtn ? 0 : fBuffer->size;
  136. return wrap + fBuffer->tail - fBuffer->wrtn - 1;
  137. }
  138. // ----------------------------------------------------------------------------------------------------------------
  139. bool readBool() noexcept
  140. {
  141. bool b = false;
  142. return tryRead(&b, sizeof(bool)) ? b : false;
  143. }
  144. uint8_t readByte() noexcept
  145. {
  146. uint8_t B = 0;
  147. return tryRead(&B, sizeof(uint8_t)) ? B : 0;
  148. }
  149. int16_t readShort() noexcept
  150. {
  151. int16_t s = 0;
  152. return tryRead(&s, sizeof(int16_t)) ? s : 0;
  153. }
  154. uint16_t readUShort() noexcept
  155. {
  156. uint16_t us = 0;
  157. return tryRead(&us, sizeof(uint16_t)) ? us : 0;
  158. }
  159. int32_t readInt() noexcept
  160. {
  161. int32_t i = 0;
  162. return tryRead(&i, sizeof(int32_t)) ? i : 0;
  163. }
  164. uint32_t readUInt() noexcept
  165. {
  166. uint32_t ui = 0;
  167. return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
  168. }
  169. int64_t readLong() noexcept
  170. {
  171. int64_t l = 0;
  172. return tryRead(&l, sizeof(int64_t)) ? l : 0;
  173. }
  174. uint64_t readULong() noexcept
  175. {
  176. uint64_t ul = 0;
  177. return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
  178. }
  179. float readFloat() noexcept
  180. {
  181. float f = 0.0f;
  182. return tryRead(&f, sizeof(float)) ? f : 0.0f;
  183. }
  184. double readDouble() noexcept
  185. {
  186. double d = 0.0;
  187. return tryRead(&d, sizeof(double)) ? d : 0.0;
  188. }
  189. void readCustomData(void* const data, const uint32_t size) noexcept
  190. {
  191. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  192. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  193. if (! tryRead(data, size))
  194. std::memset(data, 0, size);
  195. }
  196. template <typename T>
  197. void readCustomType(T& type) noexcept
  198. {
  199. if (! tryRead(&type, sizeof(T)))
  200. std::memset(&type, 0, sizeof(T));
  201. }
  202. // ----------------------------------------------------------------------------------------------------------------
  203. void skipRead(const uint32_t size) noexcept
  204. {
  205. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  206. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  207. CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size,);
  208. // empty
  209. if (fBuffer->head == fBuffer->tail)
  210. return;
  211. const uint32_t head = fBuffer->head;
  212. const uint32_t tail = fBuffer->tail;
  213. const uint32_t wrap = head > tail ? 0 : fBuffer->size;
  214. if (size > wrap + head - tail)
  215. {
  216. if (! fErrorReading)
  217. {
  218. fErrorReading = true;
  219. carla_stderr2("CarlaRingBuffer::skipRead(%u): failed, not enough space", size);
  220. }
  221. return;
  222. }
  223. uint32_t readto = tail + size;
  224. if (readto >= fBuffer->size)
  225. readto -= fBuffer->size;
  226. fBuffer->tail = readto;
  227. fErrorReading = false;
  228. return;
  229. }
  230. // ----------------------------------------------------------------------------------------------------------------
  231. bool writeBool(const bool value) noexcept
  232. {
  233. return tryWrite(&value, sizeof(bool));
  234. }
  235. bool writeByte(const uint8_t value) noexcept
  236. {
  237. return tryWrite(&value, sizeof(uint8_t));
  238. }
  239. bool writeShort(const int16_t value) noexcept
  240. {
  241. return tryWrite(&value, sizeof(int16_t));
  242. }
  243. bool writeUShort(const uint16_t value) noexcept
  244. {
  245. return tryWrite(&value, sizeof(uint16_t));
  246. }
  247. bool writeInt(const int32_t value) noexcept
  248. {
  249. return tryWrite(&value, sizeof(int32_t));
  250. }
  251. bool writeUInt(const uint32_t value) noexcept
  252. {
  253. return tryWrite(&value, sizeof(uint32_t));
  254. }
  255. bool writeLong(const int64_t value) noexcept
  256. {
  257. return tryWrite(&value, sizeof(int64_t));
  258. }
  259. bool writeULong(const uint64_t value) noexcept
  260. {
  261. return tryWrite(&value, sizeof(uint64_t));
  262. }
  263. bool writeFloat(const float value) noexcept
  264. {
  265. return tryWrite(&value, sizeof(float));
  266. }
  267. bool writeDouble(const double value) noexcept
  268. {
  269. return tryWrite(&value, sizeof(double));
  270. }
  271. bool writeCustomData(const void* const data, const uint32_t size) noexcept
  272. {
  273. CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
  274. CARLA_SAFE_ASSERT_RETURN(size > 0, false);
  275. return tryWrite(data, size);
  276. }
  277. template <typename T>
  278. bool writeCustomType(const T& type) noexcept
  279. {
  280. return tryWrite(&type, sizeof(T));
  281. }
  282. // ----------------------------------------------------------------------------------------------------------------
  283. protected:
  284. void setRingBuffer(BufferStruct* const ringBuf, const bool resetBuffer) noexcept
  285. {
  286. CARLA_SAFE_ASSERT_RETURN(fBuffer != ringBuf,);
  287. fBuffer = ringBuf;
  288. if (resetBuffer && ringBuf != nullptr)
  289. clearData();
  290. }
  291. // ----------------------------------------------------------------------------------------------------------------
  292. bool tryRead(void* const buf, const uint32_t size) noexcept
  293. {
  294. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  295. #if defined(__clang__)
  296. #pragma clang diagnostic push
  297. #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
  298. #endif
  299. CARLA_SAFE_ASSERT_RETURN(fBuffer->buf != nullptr, false);
  300. #if defined(__clang__)
  301. #pragma clang diagnostic pop
  302. #endif
  303. CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
  304. CARLA_SAFE_ASSERT_RETURN(size > 0, false);
  305. CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size, false);
  306. // empty
  307. if (fBuffer->head == fBuffer->tail)
  308. return false;
  309. uint8_t* const bytebuf = static_cast<uint8_t*>(buf);
  310. const uint32_t head = fBuffer->head;
  311. const uint32_t tail = fBuffer->tail;
  312. const uint32_t wrap = head > tail ? 0 : fBuffer->size;
  313. if (size > wrap + head - tail)
  314. {
  315. if (! fErrorReading)
  316. {
  317. fErrorReading = true;
  318. carla_stderr2("CarlaRingBuffer::tryRead(%p, %u): failed, not enough space", buf, size);
  319. }
  320. return false;
  321. }
  322. uint32_t readto = tail + size;
  323. if (readto > fBuffer->size)
  324. {
  325. readto -= fBuffer->size;
  326. if (size == 1)
  327. {
  328. std::memcpy(bytebuf, fBuffer->buf + tail, 1);
  329. }
  330. else
  331. {
  332. const uint32_t firstpart = fBuffer->size - tail;
  333. std::memcpy(bytebuf, fBuffer->buf + tail, firstpart);
  334. std::memcpy(bytebuf + firstpart, fBuffer->buf, readto);
  335. }
  336. }
  337. else
  338. {
  339. std::memcpy(bytebuf, fBuffer->buf + tail, size);
  340. if (readto == fBuffer->size)
  341. readto = 0;
  342. }
  343. fBuffer->tail = readto;
  344. fErrorReading = false;
  345. return true;
  346. }
  347. bool tryWrite(const void* const buf, const uint32_t size) noexcept
  348. {
  349. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  350. CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
  351. CARLA_SAFE_ASSERT_RETURN(size > 0, false);
  352. CARLA_SAFE_ASSERT_UINT2_RETURN(size < fBuffer->size, size, fBuffer->size, false);
  353. const uint8_t* const bytebuf = static_cast<const uint8_t*>(buf);
  354. const uint32_t tail = fBuffer->tail;
  355. const uint32_t wrtn = fBuffer->wrtn;
  356. const uint32_t wrap = tail > wrtn ? 0 : fBuffer->size;
  357. if (size >= wrap + tail - wrtn)
  358. {
  359. if (! fErrorWriting)
  360. {
  361. fErrorWriting = true;
  362. carla_stderr2("CarlaRingBuffer::tryWrite(%p, %u): failed, not enough space", buf, size);
  363. }
  364. fBuffer->invalidateCommit = true;
  365. return false;
  366. }
  367. uint32_t writeto = wrtn + size;
  368. if (writeto > fBuffer->size)
  369. {
  370. writeto -= fBuffer->size;
  371. if (size == 1)
  372. {
  373. std::memcpy(fBuffer->buf, bytebuf, 1);
  374. }
  375. else
  376. {
  377. const uint32_t firstpart = fBuffer->size - wrtn;
  378. std::memcpy(fBuffer->buf + wrtn, bytebuf, firstpart);
  379. std::memcpy(fBuffer->buf, bytebuf + firstpart, writeto);
  380. }
  381. }
  382. else
  383. {
  384. std::memcpy(fBuffer->buf + wrtn, bytebuf, size);
  385. if (writeto == fBuffer->size)
  386. writeto = 0;
  387. }
  388. fBuffer->wrtn = writeto;
  389. return true;
  390. }
  391. private:
  392. BufferStruct* fBuffer;
  393. // wherever read/write errors have been printed to terminal
  394. bool fErrorReading;
  395. bool fErrorWriting;
  396. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  397. CARLA_DECLARE_NON_COPYABLE(CarlaRingBufferControl)
  398. };
  399. template <class BufferStruct>
  400. inline bool CarlaRingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
  401. {
  402. return (fBuffer != nullptr && fBuffer->head != fBuffer->tail);
  403. }
  404. template <>
  405. inline bool CarlaRingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
  406. {
  407. return (fBuffer != nullptr && fBuffer->buf != nullptr && fBuffer->head != fBuffer->tail);
  408. }
  409. // --------------------------------------------------------------------------------------------------------------------
  410. // CarlaRingBuffer using heap space
  411. class CarlaHeapRingBuffer : public CarlaRingBufferControl<HeapBuffer>
  412. {
  413. public:
  414. CarlaHeapRingBuffer() noexcept
  415. : fHeapBuffer{0, 0, 0, 0, false, nullptr} {}
  416. ~CarlaHeapRingBuffer() noexcept override
  417. {
  418. if (fHeapBuffer.buf == nullptr)
  419. return;
  420. delete[] fHeapBuffer.buf;
  421. fHeapBuffer.buf = nullptr;
  422. }
  423. void createBuffer(const uint32_t size, const bool mlock) noexcept
  424. {
  425. CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf == nullptr,);
  426. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  427. const uint32_t p2size = carla_nextPowerOf2(size);
  428. try {
  429. fHeapBuffer.buf = new uint8_t[p2size];
  430. } CARLA_SAFE_EXCEPTION_RETURN("CarlaHeapRingBuffer::createBuffer",);
  431. fHeapBuffer.size = p2size;
  432. setRingBuffer(&fHeapBuffer, true);
  433. if (mlock)
  434. {
  435. carla_mlock(&fHeapBuffer, sizeof(fHeapBuffer));
  436. carla_mlock(fHeapBuffer.buf, p2size);
  437. }
  438. }
  439. void deleteBuffer() noexcept
  440. {
  441. if (fHeapBuffer.buf == nullptr)
  442. return;
  443. setRingBuffer(nullptr, false);
  444. delete[] fHeapBuffer.buf;
  445. fHeapBuffer.buf = nullptr;
  446. fHeapBuffer.size = 0;
  447. }
  448. private:
  449. HeapBuffer fHeapBuffer;
  450. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  451. CARLA_DECLARE_NON_COPYABLE(CarlaHeapRingBuffer)
  452. };
  453. // --------------------------------------------------------------------------------------------------------------------
  454. // CarlaRingBuffer using small stack space
  455. class CarlaSmallStackRingBuffer : public CarlaRingBufferControl<SmallStackBuffer>
  456. {
  457. public:
  458. CarlaSmallStackRingBuffer() noexcept
  459. : fStackBuffer{0, 0, 0, false, {}}
  460. {
  461. setRingBuffer(&fStackBuffer, true);
  462. }
  463. private:
  464. SmallStackBuffer fStackBuffer;
  465. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  466. CARLA_DECLARE_NON_COPYABLE(CarlaSmallStackRingBuffer)
  467. };
  468. // --------------------------------------------------------------------------------------------------------------------
  469. #endif // CARLA_RING_BUFFER_HPP_INCLUDED