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.

473 lines
12KB

  1. /*
  2. * Carla Ring Buffer
  3. * Copyright (C) 2013-2014 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 StackBuffer {
  54. static 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 const uint32_t size = 16384;
  61. uint32_t head, tail, wrtn;
  62. bool invalidateCommit;
  63. uint8_t buf[size];
  64. };
  65. #ifdef CARLA_PROPER_CPP11_SUPPORT
  66. # define HeapBuffer_INIT {0, 0, 0, 0, false, nullptr}
  67. # define StackBuffer_INIT {0, 0, 0, false, {0}}
  68. #else
  69. # define HeapBuffer_INIT
  70. # define StackBuffer_INIT
  71. #endif
  72. // -----------------------------------------------------------------------
  73. // CarlaRingBuffer templated class
  74. template <class BufferStruct>
  75. class CarlaRingBuffer
  76. {
  77. public:
  78. CarlaRingBuffer() noexcept
  79. : fBuffer(nullptr) {}
  80. virtual ~CarlaRingBuffer() noexcept {}
  81. // -------------------------------------------------------------------
  82. void clear() noexcept
  83. {
  84. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  85. fBuffer->head = 0;
  86. fBuffer->tail = 0;
  87. fBuffer->wrtn = 0;
  88. fBuffer->invalidateCommit = false;
  89. carla_zeroBytes(fBuffer->buf, fBuffer->size);
  90. }
  91. // -------------------------------------------------------------------
  92. bool commitWrite() noexcept
  93. {
  94. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  95. if (fBuffer->invalidateCommit)
  96. {
  97. fBuffer->wrtn = fBuffer->head;
  98. fBuffer->invalidateCommit = false;
  99. return false;
  100. }
  101. // nothing to commit?
  102. CARLA_SAFE_ASSERT_RETURN(fBuffer->head != fBuffer->wrtn, false);
  103. // all ok
  104. fBuffer->head = fBuffer->wrtn;
  105. return true;
  106. }
  107. bool isDataAvailableForReading() const noexcept
  108. {
  109. return (fBuffer != nullptr && fBuffer->buf != nullptr && fBuffer->head != fBuffer->tail);
  110. }
  111. bool isEmpty() const noexcept
  112. {
  113. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  114. return (fBuffer->buf == nullptr || fBuffer->head == fBuffer->tail);
  115. }
  116. // -------------------------------------------------------------------
  117. bool readBool() noexcept
  118. {
  119. bool b = false;
  120. return tryRead(&b, sizeof(bool)) ? b : false;
  121. }
  122. uint8_t readByte() noexcept
  123. {
  124. uint8_t B = 0;
  125. return tryRead(&B, sizeof(uint8_t)) ? B : 0;
  126. }
  127. int16_t readShort() noexcept
  128. {
  129. int16_t s = 0;
  130. return tryRead(&s, sizeof(int16_t)) ? s : 0;
  131. }
  132. uint16_t readUShort() noexcept
  133. {
  134. uint16_t us = 0;
  135. return tryRead(&us, sizeof(uint16_t)) ? us : 0;
  136. }
  137. int32_t readInt() noexcept
  138. {
  139. int32_t i = 0;
  140. return tryRead(&i, sizeof(int32_t)) ? i : 0;
  141. }
  142. uint32_t readUInt() noexcept
  143. {
  144. uint32_t ui = 0;
  145. return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
  146. }
  147. int64_t readLong() noexcept
  148. {
  149. int64_t l = 0;
  150. return tryRead(&l, sizeof(int64_t)) ? l : 0;
  151. }
  152. uint64_t readULong() noexcept
  153. {
  154. uint64_t ul = 0;
  155. return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
  156. }
  157. float readFloat() noexcept
  158. {
  159. float f = 0.0f;
  160. return tryRead(&f, sizeof(float)) ? f : 0.0f;
  161. }
  162. double readDouble() noexcept
  163. {
  164. double d = 0.0;
  165. return tryRead(&d, sizeof(double)) ? d : 0.0;
  166. }
  167. void readCustomData(void* const data, const uint32_t size) noexcept
  168. {
  169. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  170. if (! tryRead(data, size))
  171. carla_zeroBytes(data, size);
  172. }
  173. template <typename T>
  174. void readCustomType(T& type) noexcept
  175. {
  176. if (! tryRead(&type, sizeof(T)))
  177. carla_zeroStruct(type);
  178. }
  179. // -------------------------------------------------------------------
  180. void writeBool(const bool value) noexcept
  181. {
  182. tryWrite(&value, sizeof(bool));
  183. }
  184. void writeByte(const uint8_t value) noexcept
  185. {
  186. tryWrite(&value, sizeof(uint8_t));
  187. }
  188. void writeShort(const int16_t value) noexcept
  189. {
  190. tryWrite(&value, sizeof(int16_t));
  191. }
  192. void writeUShort(const uint16_t value) noexcept
  193. {
  194. tryWrite(&value, sizeof(uint16_t));
  195. }
  196. void writeInt(const int32_t value) noexcept
  197. {
  198. tryWrite(&value, sizeof(int32_t));
  199. }
  200. void writeUInt(const uint32_t value) noexcept
  201. {
  202. tryWrite(&value, sizeof(uint32_t));
  203. }
  204. void writeLong(const int64_t value) noexcept
  205. {
  206. tryWrite(&value, sizeof(int64_t));
  207. }
  208. void writeFloat(const float value) noexcept
  209. {
  210. tryWrite(&value, sizeof(float));
  211. }
  212. void writeDouble(const double value) noexcept
  213. {
  214. tryWrite(&value, sizeof(double));
  215. }
  216. void writeCustomData(const void* const value, const uint32_t size) noexcept
  217. {
  218. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  219. tryWrite(value, size);
  220. }
  221. template <typename T>
  222. void writeCustomType(const T& value) noexcept
  223. {
  224. tryWrite(&value, sizeof(T));
  225. }
  226. // -------------------------------------------------------------------
  227. protected:
  228. void setRingBuffer(BufferStruct* const ringBuf, const bool reset) noexcept
  229. {
  230. CARLA_SAFE_ASSERT_RETURN(fBuffer != ringBuf,);
  231. fBuffer = ringBuf;
  232. if (reset && ringBuf != nullptr)
  233. clear();
  234. }
  235. // -------------------------------------------------------------------
  236. bool tryRead(void* const buf, const uint32_t size) noexcept
  237. {
  238. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
  239. CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
  240. CARLA_SAFE_ASSERT_RETURN(size > 0, false);
  241. CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size, false);
  242. // empty
  243. if (fBuffer->head == fBuffer->tail)
  244. return false;
  245. uint8_t* const bytebuf(static_cast<uint8_t*>(buf));
  246. const uint32_t head(fBuffer->head);
  247. const uint32_t tail(fBuffer->tail);
  248. const uint32_t wrap((head > tail) ? 0 : fBuffer->size);
  249. if (size > wrap + head - tail)
  250. {
  251. carla_stderr2("CarlaRingBuffer::tryRead(%p, " P_SIZE "): failed, not enough space", buf, size);
  252. return false;
  253. }
  254. uint32_t readto(tail + size);
  255. if (readto > fBuffer->size)
  256. {
  257. readto -= fBuffer->size;
  258. const uint32_t firstpart(fBuffer->size - tail);
  259. std::memcpy(bytebuf, fBuffer->buf + tail, firstpart);
  260. std::memcpy(bytebuf + firstpart, fBuffer->buf, readto);
  261. }
  262. else
  263. {
  264. std::memcpy(bytebuf, fBuffer->buf + tail, size);
  265. if (readto == fBuffer->size)
  266. readto = 0;
  267. }
  268. fBuffer->tail = readto;
  269. return true;
  270. }
  271. void tryWrite(const void* const buf, const uint32_t size) noexcept
  272. {
  273. CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
  274. CARLA_SAFE_ASSERT_RETURN(buf != nullptr,);
  275. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  276. CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size,);
  277. const uint8_t* const bytebuf(static_cast<const uint8_t*>(buf));
  278. const uint32_t tail(fBuffer->tail);
  279. const uint32_t wrtn(fBuffer->wrtn);
  280. const uint32_t wrap((tail > wrtn) ? 0 : fBuffer->size);
  281. if (size >= wrap + tail - wrtn)
  282. {
  283. carla_stderr2("CarlaRingBuffer::tryWrite(%p, " P_SIZE "): failed, not enough space", buf, size);
  284. fBuffer->invalidateCommit = true;
  285. return;
  286. }
  287. uint32_t writeto(wrtn + size);
  288. if (writeto > fBuffer->size)
  289. {
  290. writeto -= fBuffer->size;
  291. const uint32_t firstpart(fBuffer->size - wrtn);
  292. std::memcpy(fBuffer->buf + wrtn, bytebuf, firstpart);
  293. std::memcpy(fBuffer->buf, bytebuf + firstpart, writeto);
  294. }
  295. else
  296. {
  297. std::memcpy(fBuffer->buf + wrtn, bytebuf, size);
  298. if (writeto == fBuffer->size)
  299. writeto = 0;
  300. }
  301. fBuffer->wrtn = writeto;
  302. }
  303. private:
  304. BufferStruct* fBuffer;
  305. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  306. CARLA_DECLARE_NON_COPY_CLASS(CarlaRingBuffer)
  307. };
  308. // -----------------------------------------------------------------------
  309. // CarlaRingBuffer using heap space
  310. class CarlaHeapRingBuffer : public CarlaRingBuffer<HeapBuffer>
  311. {
  312. public:
  313. CarlaHeapRingBuffer() noexcept
  314. #ifdef CARLA_PROPER_CPP11_SUPPORT
  315. : fHeapBuffer(HeapBuffer_INIT) {}
  316. #else
  317. {
  318. carla_zeroStruct(fHeapBuffer);
  319. }
  320. #endif
  321. ~CarlaHeapRingBuffer() noexcept override
  322. {
  323. if (fHeapBuffer.buf == nullptr)
  324. return;
  325. delete[] fHeapBuffer.buf;
  326. fHeapBuffer.buf = nullptr;
  327. }
  328. void createBuffer(const uint32_t size) noexcept
  329. {
  330. CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf == nullptr,);
  331. CARLA_SAFE_ASSERT_RETURN(size > 0,);
  332. const uint32_t p2size(carla_nextPowerOf2(size));
  333. try {
  334. fHeapBuffer.buf = new uint8_t[p2size];
  335. } CARLA_SAFE_EXCEPTION_RETURN("CarlaHeapRingBuffer::createBuffer",);
  336. fHeapBuffer.size = p2size;
  337. setRingBuffer(&fHeapBuffer, true);
  338. }
  339. void deleteBuffer() noexcept
  340. {
  341. CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf != nullptr,);
  342. setRingBuffer(nullptr, false);
  343. delete[] fHeapBuffer.buf;
  344. fHeapBuffer.buf = nullptr;
  345. fHeapBuffer.size = 0;
  346. }
  347. private:
  348. HeapBuffer fHeapBuffer;
  349. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  350. CARLA_DECLARE_NON_COPY_CLASS(CarlaHeapRingBuffer)
  351. };
  352. // -----------------------------------------------------------------------
  353. // CarlaRingBuffer using stack space
  354. class CarlaStackRingBuffer : public CarlaRingBuffer<StackBuffer>
  355. {
  356. public:
  357. CarlaStackRingBuffer() noexcept
  358. #ifdef CARLA_PROPER_CPP11_SUPPORT
  359. : fStackBuffer(StackBuffer_INIT)
  360. {
  361. setRingBuffer(&fStackBuffer, false);
  362. }
  363. #else
  364. {
  365. setRingBuffer(&fStackBuffer, true);
  366. }
  367. #endif
  368. private:
  369. StackBuffer fStackBuffer;
  370. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  371. CARLA_DECLARE_NON_COPY_CLASS(CarlaStackRingBuffer)
  372. };
  373. // -----------------------------------------------------------------------
  374. #endif // CARLA_RING_BUFFER_HPP_INCLUDED