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.

573 lines
15KB

  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. #ifdef CARLA_PROPER_CPP11_SUPPORT
  72. # define HeapBuffer_INIT {0, 0, 0, 0, false, nullptr}
  73. # define StackBuffer_INIT {0, 0, 0, false, {0}}
  74. #else
  75. # define HeapBuffer_INIT
  76. # define StackBuffer_INIT
  77. #endif
  78. // -----------------------------------------------------------------------
  79. // CarlaRingBufferControl templated class
  80. template <class BufferStruct>
  81. class CarlaRingBufferControl
  82. {
  83. public:
  84. CarlaRingBufferControl() noexcept
  85. : fBuffer(nullptr),
  86. fErrorReading(false),
  87. fErrorWriting(false) {}
  88. virtual ~CarlaRingBufferControl() noexcept {}
  89. // -------------------------------------------------------------------
  90. void clearData() noexcept
  91. {
  92. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  93. fBuffer->head = 0;
  94. fBuffer->tail = 0;
  95. fBuffer->wrtn = 0;
  96. fBuffer->invalidateCommit = false;
  97. carla_zeroBytes(fBuffer->buf, fBuffer->size);
  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 getAvailableDataSize() const noexcept
  123. {
  124. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0);
  125. const uint32_t wrap((fBuffer->tail > fBuffer->wrtn) ? 0 : fBuffer->size);
  126. return wrap + fBuffer->tail - fBuffer->wrtn;
  127. }
  128. // -------------------------------------------------------------------
  129. bool readBool() noexcept
  130. {
  131. bool b = false;
  132. return tryRead(&b, sizeof(bool)) ? b : false;
  133. }
  134. uint8_t readByte() noexcept
  135. {
  136. uint8_t B = 0;
  137. return tryRead(&B, sizeof(uint8_t)) ? B : 0;
  138. }
  139. int16_t readShort() noexcept
  140. {
  141. int16_t s = 0;
  142. return tryRead(&s, sizeof(int16_t)) ? s : 0;
  143. }
  144. uint16_t readUShort() noexcept
  145. {
  146. uint16_t us = 0;
  147. return tryRead(&us, sizeof(uint16_t)) ? us : 0;
  148. }
  149. int32_t readInt() noexcept
  150. {
  151. int32_t i = 0;
  152. return tryRead(&i, sizeof(int32_t)) ? i : 0;
  153. }
  154. uint32_t readUInt() noexcept
  155. {
  156. uint32_t ui = 0;
  157. return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
  158. }
  159. int64_t readLong() noexcept
  160. {
  161. int64_t l = 0;
  162. return tryRead(&l, sizeof(int64_t)) ? l : 0;
  163. }
  164. uint64_t readULong() noexcept
  165. {
  166. uint64_t ul = 0;
  167. return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
  168. }
  169. float readFloat() noexcept
  170. {
  171. float f = 0.0f;
  172. return tryRead(&f, sizeof(float)) ? f : 0.0f;
  173. }
  174. double readDouble() noexcept
  175. {
  176. double d = 0.0;
  177. return tryRead(&d, sizeof(double)) ? d : 0.0;
  178. }
  179. void readCustomData(void* const data, const uint32_t size) noexcept
  180. {
  181. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  182. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  183. if (! tryRead(data, size))
  184. std::memset(data, 0, size);
  185. }
  186. template <typename T>
  187. void readCustomType(T& type) noexcept
  188. {
  189. if (! tryRead(&type, sizeof(T)))
  190. std::memset(&type, 0, sizeof(T));
  191. }
  192. // -------------------------------------------------------------------
  193. void skipRead(const uint32_t size) noexcept
  194. {
  195. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  196. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  197. CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size,);
  198. // empty
  199. if (fBuffer->head == fBuffer->tail)
  200. return;
  201. const uint32_t head = fBuffer->head;
  202. const uint32_t tail = fBuffer->tail;
  203. const uint32_t wrap = head > tail ? 0 : fBuffer->size;
  204. if (size > wrap + head - tail)
  205. {
  206. if (! fErrorReading)
  207. {
  208. fErrorReading = true;
  209. carla_stderr2("CarlaRingBuffer::skipRead(%u): failed, not enough space", size);
  210. }
  211. return;
  212. }
  213. uint32_t readto = tail + size;
  214. if (readto >= fBuffer->size)
  215. readto -= fBuffer->size;
  216. fBuffer->tail = readto;
  217. fErrorReading = false;
  218. return;
  219. }
  220. // -------------------------------------------------------------------
  221. bool writeBool(const bool value) noexcept
  222. {
  223. return tryWrite(&value, sizeof(bool));
  224. }
  225. bool writeByte(const uint8_t value) noexcept
  226. {
  227. return tryWrite(&value, sizeof(uint8_t));
  228. }
  229. bool writeShort(const int16_t value) noexcept
  230. {
  231. return tryWrite(&value, sizeof(int16_t));
  232. }
  233. bool writeUShort(const uint16_t value) noexcept
  234. {
  235. return tryWrite(&value, sizeof(uint16_t));
  236. }
  237. bool writeInt(const int32_t value) noexcept
  238. {
  239. return tryWrite(&value, sizeof(int32_t));
  240. }
  241. bool writeUInt(const uint32_t value) noexcept
  242. {
  243. return tryWrite(&value, sizeof(uint32_t));
  244. }
  245. bool writeLong(const int64_t value) noexcept
  246. {
  247. return tryWrite(&value, sizeof(int64_t));
  248. }
  249. bool writeULong(const uint64_t value) noexcept
  250. {
  251. return tryWrite(&value, sizeof(uint64_t));
  252. }
  253. bool writeFloat(const float value) noexcept
  254. {
  255. return tryWrite(&value, sizeof(float));
  256. }
  257. bool writeDouble(const double value) noexcept
  258. {
  259. return tryWrite(&value, sizeof(double));
  260. }
  261. bool writeCustomData(const void* const data, const uint32_t size) noexcept
  262. {
  263. CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
  264. CARLA_SAFE_ASSERT_RETURN(size > 0, false);
  265. return tryWrite(data, size);
  266. }
  267. template <typename T>
  268. bool writeCustomType(const T& type) noexcept
  269. {
  270. return tryWrite(&type, sizeof(T));
  271. }
  272. // -------------------------------------------------------------------
  273. protected:
  274. void setRingBuffer(BufferStruct* const ringBuf, const bool resetBuffer) noexcept
  275. {
  276. CARLA_SAFE_ASSERT_RETURN(fBuffer != ringBuf,);
  277. fBuffer = ringBuf;
  278. if (resetBuffer && ringBuf != nullptr)
  279. clearData();
  280. }
  281. // -------------------------------------------------------------------
  282. bool tryRead(void* const buf, const uint32_t size) noexcept
  283. {
  284. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  285. #if defined(__clang__)
  286. #pragma clang diagnostic push
  287. #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
  288. #endif
  289. CARLA_SAFE_ASSERT_RETURN(fBuffer->buf != nullptr, false);
  290. #if defined(__clang__)
  291. #pragma clang diagnostic pop
  292. #endif
  293. CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
  294. CARLA_SAFE_ASSERT_RETURN(size > 0, false);
  295. CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size, false);
  296. // empty
  297. if (fBuffer->head == fBuffer->tail)
  298. return false;
  299. uint8_t* const bytebuf = static_cast<uint8_t*>(buf);
  300. const uint32_t head = fBuffer->head;
  301. const uint32_t tail = fBuffer->tail;
  302. const uint32_t wrap = head > tail ? 0 : fBuffer->size;
  303. if (size > wrap + head - tail)
  304. {
  305. if (! fErrorReading)
  306. {
  307. fErrorReading = true;
  308. carla_stderr2("CarlaRingBuffer::tryRead(%p, %u): failed, not enough space", buf, size);
  309. }
  310. return false;
  311. }
  312. uint32_t readto = tail + size;
  313. if (readto > fBuffer->size)
  314. {
  315. readto -= fBuffer->size;
  316. if (size == 1)
  317. {
  318. std::memcpy(bytebuf, fBuffer->buf + tail, 1);
  319. }
  320. else
  321. {
  322. const uint32_t firstpart = fBuffer->size - tail;
  323. std::memcpy(bytebuf, fBuffer->buf + tail, firstpart);
  324. std::memcpy(bytebuf + firstpart, fBuffer->buf, readto);
  325. }
  326. }
  327. else
  328. {
  329. std::memcpy(bytebuf, fBuffer->buf + tail, size);
  330. if (readto == fBuffer->size)
  331. readto = 0;
  332. }
  333. fBuffer->tail = readto;
  334. fErrorReading = false;
  335. return true;
  336. }
  337. bool tryWrite(const void* const buf, const uint32_t size) noexcept
  338. {
  339. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  340. CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
  341. CARLA_SAFE_ASSERT_RETURN(size > 0, false);
  342. CARLA_SAFE_ASSERT_UINT2_RETURN(size < fBuffer->size, size, fBuffer->size, false);
  343. const uint8_t* const bytebuf = static_cast<const uint8_t*>(buf);
  344. const uint32_t tail = fBuffer->tail;
  345. const uint32_t wrtn = fBuffer->wrtn;
  346. const uint32_t wrap = tail > wrtn ? 0 : fBuffer->size;
  347. if (size >= wrap + tail - wrtn)
  348. {
  349. if (! fErrorWriting)
  350. {
  351. fErrorWriting = true;
  352. carla_stderr2("CarlaRingBuffer::tryWrite(%p, %u): failed, not enough space", buf, size);
  353. }
  354. fBuffer->invalidateCommit = true;
  355. return false;
  356. }
  357. uint32_t writeto = wrtn + size;
  358. if (writeto > fBuffer->size)
  359. {
  360. writeto -= fBuffer->size;
  361. if (size == 1)
  362. {
  363. std::memcpy(fBuffer->buf, bytebuf, 1);
  364. }
  365. else
  366. {
  367. const uint32_t firstpart = fBuffer->size - wrtn;
  368. std::memcpy(fBuffer->buf + wrtn, bytebuf, firstpart);
  369. std::memcpy(fBuffer->buf, bytebuf + firstpart, writeto);
  370. }
  371. }
  372. else
  373. {
  374. std::memcpy(fBuffer->buf + wrtn, bytebuf, size);
  375. if (writeto == fBuffer->size)
  376. writeto = 0;
  377. }
  378. fBuffer->wrtn = writeto;
  379. return true;
  380. }
  381. private:
  382. BufferStruct* fBuffer;
  383. // wherever read/write errors have been printed to terminal
  384. bool fErrorReading;
  385. bool fErrorWriting;
  386. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  387. CARLA_DECLARE_NON_COPYABLE(CarlaRingBufferControl)
  388. };
  389. template <class BufferStruct>
  390. inline bool CarlaRingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
  391. {
  392. return (fBuffer != nullptr && fBuffer->head != fBuffer->tail);
  393. }
  394. template <>
  395. inline bool CarlaRingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
  396. {
  397. return (fBuffer != nullptr && fBuffer->buf != nullptr && fBuffer->head != fBuffer->tail);
  398. }
  399. // -----------------------------------------------------------------------
  400. // CarlaRingBuffer using heap space
  401. class CarlaHeapRingBuffer : public CarlaRingBufferControl<HeapBuffer>
  402. {
  403. public:
  404. CarlaHeapRingBuffer() noexcept
  405. : fHeapBuffer(HeapBuffer_INIT)
  406. {
  407. carla_zeroStruct(fHeapBuffer);
  408. }
  409. ~CarlaHeapRingBuffer() noexcept override
  410. {
  411. if (fHeapBuffer.buf == nullptr)
  412. return;
  413. delete[] fHeapBuffer.buf;
  414. fHeapBuffer.buf = nullptr;
  415. }
  416. void createBuffer(const uint32_t size) noexcept
  417. {
  418. CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf == nullptr,);
  419. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  420. const uint32_t p2size = carla_nextPowerOf2(size);
  421. try {
  422. fHeapBuffer.buf = new uint8_t[p2size];
  423. } CARLA_SAFE_EXCEPTION_RETURN("CarlaHeapRingBuffer::createBuffer",);
  424. fHeapBuffer.size = p2size;
  425. setRingBuffer(&fHeapBuffer, true);
  426. }
  427. void deleteBuffer() noexcept
  428. {
  429. CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf != nullptr,);
  430. setRingBuffer(nullptr, false);
  431. delete[] fHeapBuffer.buf;
  432. fHeapBuffer.buf = nullptr;
  433. fHeapBuffer.size = 0;
  434. }
  435. private:
  436. HeapBuffer fHeapBuffer;
  437. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  438. CARLA_DECLARE_NON_COPYABLE(CarlaHeapRingBuffer)
  439. };
  440. // -----------------------------------------------------------------------
  441. // CarlaRingBuffer using small stack space
  442. class CarlaSmallStackRingBuffer : public CarlaRingBufferControl<SmallStackBuffer>
  443. {
  444. public:
  445. CarlaSmallStackRingBuffer() noexcept
  446. : fStackBuffer(StackBuffer_INIT)
  447. {
  448. setRingBuffer(&fStackBuffer, true);
  449. }
  450. private:
  451. SmallStackBuffer fStackBuffer;
  452. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  453. CARLA_DECLARE_NON_COPYABLE(CarlaSmallStackRingBuffer)
  454. };
  455. // -----------------------------------------------------------------------
  456. #endif // CARLA_RING_BUFFER_HPP_INCLUDED