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.

456 lines
11KB

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