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.

CarlaRingBuffer.hpp 16KB

11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  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;
  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