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