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.

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