Collection of DPF-based plugins for packaging
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.

768 lines
21KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #ifndef DISTRHO_RING_BUFFER_HPP_INCLUDED
  17. #define DISTRHO_RING_BUFFER_HPP_INCLUDED
  18. #include "../DistrhoUtils.hpp"
  19. START_NAMESPACE_DISTRHO
  20. // -----------------------------------------------------------------------
  21. // Buffer structs
  22. /**
  23. Base structure for all RingBuffer containers.
  24. This struct details the data model used in DPF's RingBuffer class.
  25. DPF RingBuffer uses a struct just like this one to store positions, buffer data, size, etc.
  26. The RingBuffer itself takes ownership of this struct and uses it to store any needed data.
  27. This allows to dynamically change the way its ring buffer is allocated, simply by changing the template type.
  28. For example, `RingBufferControl<HeapBuffer>` will create a ring buffer with heap memory, which can be of any size.
  29. In the same vein, `RingBufferControl<SmallStackBuffer>` will create a ring buffer with stack memory,
  30. directly tied to the RingBufferControl it belongs to.
  31. The main idea behind this model is to allow RingBufferControl over memory created elsewhere,
  32. for example shared memory area.
  33. One can create/place the Buffer struct in shared memory, and point RingBufferControl to it,
  34. thus avoiding the pitfalls of sharing access to a non trivially-copyable/POD C++ class.
  35. Unlike other ring buffers, an extra variable is used to track pending writes.
  36. This is so we can write a few bytes at a time and later mark the whole operation as complete,
  37. thus avoiding the issue of reading data too early from the other side.
  38. For example, write the size of some data first, and then the actual data.
  39. The reading side will only see data available once size + data is completely written and "committed".
  40. */
  41. struct HeapBuffer {
  42. /**
  43. Size of the buffer, allocated in @a buf.
  44. If the size is fixed (stack buffer), this variable can be static.
  45. */
  46. uint32_t size;
  47. /**
  48. Current writing position, headmost position of the buffer.
  49. Increments when writing.
  50. */
  51. uint32_t head;
  52. /**
  53. Current reading position, last used position of the buffer.
  54. Increments when reading.
  55. head == tail means empty buffer.
  56. */
  57. uint32_t tail;
  58. /**
  59. Temporary position of head until a commitWrite() is called.
  60. If buffer writing fails, wrtn will be back to head position thus ignoring the last operation(s).
  61. If buffer writing succeeds, head will be set to this variable.
  62. */
  63. uint32_t wrtn;
  64. /**
  65. Boolean used to check if a write operation failed.
  66. This ensures we don't get incomplete writes.
  67. */
  68. bool invalidateCommit;
  69. /**
  70. Pointer to buffer data.
  71. This can be either stack or heap data, depending on the usecase.
  72. */
  73. uint8_t* buf;
  74. };
  75. /**
  76. RingBufferControl compatible struct with a relatively small stack size (4k bytes).
  77. @see HeapBuffer
  78. */
  79. struct SmallStackBuffer {
  80. static const uint32_t size = 4096;
  81. uint32_t head, tail, wrtn;
  82. bool invalidateCommit;
  83. uint8_t buf[size];
  84. };
  85. /**
  86. RingBufferControl compatible struct with a relatively big stack size (16k bytes).
  87. @see HeapBuffer
  88. */
  89. struct BigStackBuffer {
  90. static const uint32_t size = 16384;
  91. uint32_t head, tail, wrtn;
  92. bool invalidateCommit;
  93. uint8_t buf[size];
  94. };
  95. /**
  96. RingBufferControl compatible struct with a huge stack size (64k bytes).
  97. @see HeapBuffer
  98. */
  99. struct HugeStackBuffer {
  100. static const uint32_t size = 65536;
  101. uint32_t head, tail, wrtn;
  102. bool invalidateCommit;
  103. uint8_t buf[size];
  104. };
  105. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  106. # define HeapBuffer_INIT {0, 0, 0, 0, false, nullptr}
  107. # define StackBuffer_INIT {0, 0, 0, false, {0}}
  108. #else
  109. # define HeapBuffer_INIT
  110. # define StackBuffer_INIT
  111. #endif
  112. // -----------------------------------------------------------------------
  113. // RingBufferControl templated class
  114. /**
  115. DPF built-in RingBuffer class.
  116. RingBufferControl takes one buffer struct to take control over, and operates over it.
  117. This is meant for single-writer, single-reader type of control.
  118. Writing and reading is wait and lock-free.
  119. Typically usage involves:
  120. ```
  121. // definition
  122. HeapRingBuffer myHeapBuffer; // or RingBufferControl<HeapBuffer> class for more control
  123. // construction, only needed for heap buffers
  124. myHeapBuffer.createBuffer(8192);
  125. // writing data
  126. myHeapBuffer.writeUInt(size);
  127. myHeapBuffer.writeCustomData(someOtherData, size);
  128. myHeapBuffer.commitWrite();
  129. // reading data
  130. if (myHeapBuffer.isDataAvailableForReading())
  131. {
  132. uint32_t size;
  133. if (myHeapBuffer.readUInt(size) && readCustomData(&anotherData, size))
  134. {
  135. // do something with "anotherData"
  136. }
  137. }
  138. ```
  139. @see HeapBuffer
  140. */
  141. template <class BufferStruct>
  142. class RingBufferControl
  143. {
  144. public:
  145. /*
  146. * Constructor for uninitialised ring buffer.
  147. * A call to setRingBuffer is required to tied this control to a ring buffer struct;
  148. *
  149. */
  150. RingBufferControl() noexcept
  151. : buffer(nullptr),
  152. errorReading(false),
  153. errorWriting(false) {}
  154. /*
  155. * Destructor.
  156. */
  157. virtual ~RingBufferControl() noexcept {}
  158. // -------------------------------------------------------------------
  159. // check operations
  160. /*
  161. * Check if there is any data available for reading, regardless of size.
  162. */
  163. bool isDataAvailableForReading() const noexcept;
  164. /*
  165. * Check if ring buffer is empty (that is, there is nothing to read).
  166. */
  167. bool isEmpty() const noexcept
  168. {
  169. DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
  170. return (buffer->buf == nullptr || buffer->head == buffer->tail);
  171. }
  172. /*
  173. * Get the size of the data available to read.
  174. */
  175. uint32_t getAvailableDataSize() const noexcept
  176. {
  177. DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, 0);
  178. const uint32_t wrap((buffer->tail > buffer->wrtn) ? 0 : buffer->size);
  179. return wrap + buffer->tail - buffer->wrtn;
  180. }
  181. // -------------------------------------------------------------------
  182. // clear/reset operations
  183. /*
  184. * Clear the entire ring buffer data, marking the buffer as empty.
  185. * Requires a buffer struct tied to this class.
  186. */
  187. void clearData() noexcept
  188. {
  189. DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr,);
  190. buffer->head = 0;
  191. buffer->tail = 0;
  192. buffer->wrtn = 0;
  193. buffer->invalidateCommit = false;
  194. std::memset(buffer->buf, 0, buffer->size);
  195. }
  196. // -------------------------------------------------------------------
  197. // read operations
  198. /*
  199. * Read a single boolean value.
  200. * Returns false if reading fails.
  201. */
  202. bool readBool() noexcept
  203. {
  204. bool b = false;
  205. return tryRead(&b, sizeof(bool)) ? b : false;
  206. }
  207. /*
  208. * Read a single 8-bit byte.
  209. * Returns 0 if reading fails.
  210. */
  211. uint8_t readByte() noexcept
  212. {
  213. uint8_t B = 0;
  214. return tryRead(&B, sizeof(uint8_t)) ? B : 0;
  215. }
  216. /*
  217. * Read a short 16-bit integer.
  218. * Returns 0 if reading fails.
  219. */
  220. int16_t readShort() noexcept
  221. {
  222. int16_t s = 0;
  223. return tryRead(&s, sizeof(int16_t)) ? s : 0;
  224. }
  225. /*
  226. * Read a short unsigned 16-bit integer.
  227. * Returns 0 if reading fails.
  228. */
  229. uint16_t readUShort() noexcept
  230. {
  231. uint16_t us = 0;
  232. return tryRead(&us, sizeof(uint16_t)) ? us : 0;
  233. }
  234. /*
  235. * Read a regular 32-bit integer.
  236. * Returns 0 if reading fails.
  237. */
  238. int32_t readInt() noexcept
  239. {
  240. int32_t i = 0;
  241. return tryRead(&i, sizeof(int32_t)) ? i : 0;
  242. }
  243. /*
  244. * Read an unsigned 32-bit integer.
  245. * Returns 0 if reading fails.
  246. */
  247. uint32_t readUInt() noexcept
  248. {
  249. uint32_t ui = 0;
  250. return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
  251. }
  252. /*
  253. * Read a long 64-bit integer.
  254. * Returns 0 if reading fails.
  255. */
  256. int64_t readLong() noexcept
  257. {
  258. int64_t l = 0;
  259. return tryRead(&l, sizeof(int64_t)) ? l : 0;
  260. }
  261. /*
  262. * Read a long unsigned 64-bit integer.
  263. * Returns 0 if reading fails.
  264. */
  265. uint64_t readULong() noexcept
  266. {
  267. uint64_t ul = 0;
  268. return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
  269. }
  270. /*
  271. * Read a single-precision floating point number.
  272. * Returns 0 if reading fails.
  273. */
  274. float readFloat() noexcept
  275. {
  276. float f = 0.0f;
  277. return tryRead(&f, sizeof(float)) ? f : 0.0f;
  278. }
  279. /*
  280. * Read a double-precision floating point number.
  281. * Returns 0 if reading fails.
  282. */
  283. double readDouble() noexcept
  284. {
  285. double d = 0.0;
  286. return tryRead(&d, sizeof(double)) ? d : 0.0;
  287. }
  288. /*!
  289. * Read an arbitrary amount of data, specified by @a size.
  290. * data pointer must be non-null, and size > 0.
  291. *
  292. * Returns true if reading succeeds.
  293. * In case of failure, @a data pointer is automatically cleared by @a size bytes.
  294. */
  295. bool readCustomData(void* const data, const uint32_t size) noexcept
  296. {
  297. DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, false);
  298. DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
  299. if (tryRead(data, size))
  300. return true;
  301. std::memset(data, 0, size);
  302. return false;
  303. }
  304. /*!
  305. * Read a custom data type specified by the template typename used,
  306. * with size being automatically deduced by the compiler (through the use of sizeof).
  307. *
  308. * Returns true if reading succeeds.
  309. * In case of failure, @a type value is automatically cleared by its deduced size.
  310. */
  311. template <typename T>
  312. bool readCustomType(T& type) noexcept
  313. {
  314. if (tryRead(&type, sizeof(T)))
  315. return true;
  316. std::memset(&type, 0, sizeof(T));
  317. return false;
  318. }
  319. // -------------------------------------------------------------------
  320. // write operations
  321. /*
  322. * Write a single boolean value.
  323. */
  324. bool writeBool(const bool value) noexcept
  325. {
  326. return tryWrite(&value, sizeof(bool));
  327. }
  328. /*
  329. * Write a single 8-bit byte.
  330. */
  331. bool writeByte(const uint8_t value) noexcept
  332. {
  333. return tryWrite(&value, sizeof(uint8_t));
  334. }
  335. /*
  336. * Write a short 16-bit integer.
  337. */
  338. bool writeShort(const int16_t value) noexcept
  339. {
  340. return tryWrite(&value, sizeof(int16_t));
  341. }
  342. /*
  343. * Write a short unsigned 16-bit integer.
  344. */
  345. bool writeUShort(const uint16_t value) noexcept
  346. {
  347. return tryWrite(&value, sizeof(uint16_t));
  348. }
  349. /*
  350. * Write a regular 32-bit integer.
  351. */
  352. bool writeInt(const int32_t value) noexcept
  353. {
  354. return tryWrite(&value, sizeof(int32_t));
  355. }
  356. /*
  357. * Write an unsigned 32-bit integer.
  358. */
  359. bool writeUInt(const uint32_t value) noexcept
  360. {
  361. return tryWrite(&value, sizeof(uint32_t));
  362. }
  363. /*
  364. * Write a long 64-bit integer.
  365. */
  366. bool writeLong(const int64_t value) noexcept
  367. {
  368. return tryWrite(&value, sizeof(int64_t));
  369. }
  370. /*
  371. * Write a long unsigned 64-bit integer.
  372. */
  373. bool writeULong(const uint64_t value) noexcept
  374. {
  375. return tryWrite(&value, sizeof(uint64_t));
  376. }
  377. /*
  378. * Write a single-precision floating point number.
  379. */
  380. bool writeFloat(const float value) noexcept
  381. {
  382. return tryWrite(&value, sizeof(float));
  383. }
  384. /*
  385. * Write a double-precision floating point number.
  386. */
  387. bool writeDouble(const double value) noexcept
  388. {
  389. return tryWrite(&value, sizeof(double));
  390. }
  391. /*!
  392. * Write an arbitrary amount of data, specified by @a size.
  393. * data pointer must be non-null, and size > 0.
  394. */
  395. bool writeCustomData(const void* const data, const uint32_t size) noexcept
  396. {
  397. DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, false);
  398. DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
  399. return tryWrite(data, size);
  400. }
  401. /*!
  402. * Write a custom data type specified by the template typename used,
  403. * with size being automatically deduced by the compiler (through the use of sizeof).
  404. */
  405. template <typename T>
  406. bool writeCustomType(const T& type) noexcept
  407. {
  408. return tryWrite(&type, sizeof(T));
  409. }
  410. // -------------------------------------------------------------------
  411. /*!
  412. * Commit all previous write operations to the ringbuffer.
  413. * If a write operation has previously failed, this will reset/invalidate the previous write attempts.
  414. */
  415. bool commitWrite() noexcept
  416. {
  417. DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
  418. if (buffer->invalidateCommit)
  419. {
  420. buffer->wrtn = buffer->head;
  421. buffer->invalidateCommit = false;
  422. return false;
  423. }
  424. // nothing to commit?
  425. DISTRHO_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
  426. // all ok
  427. buffer->head = buffer->wrtn;
  428. errorWriting = false;
  429. return true;
  430. }
  431. // -------------------------------------------------------------------
  432. /*
  433. * Tie this ring buffer control to a ring buffer struct, optionally clearing its data.
  434. */
  435. void setRingBuffer(BufferStruct* const ringBuf, const bool clearRingBufferData) noexcept
  436. {
  437. DISTRHO_SAFE_ASSERT_RETURN(buffer != ringBuf,);
  438. buffer = ringBuf;
  439. if (clearRingBufferData && ringBuf != nullptr)
  440. clearData();
  441. }
  442. // -------------------------------------------------------------------
  443. protected:
  444. /** @internal try reading from the buffer, can fail. */
  445. bool tryRead(void* const buf, const uint32_t size) noexcept
  446. {
  447. DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
  448. #if defined(__clang__)
  449. # pragma clang diagnostic push
  450. # pragma clang diagnostic ignored "-Wtautological-pointer-compare"
  451. #endif
  452. DISTRHO_SAFE_ASSERT_RETURN(buffer->buf != nullptr, false);
  453. #if defined(__clang__)
  454. # pragma clang diagnostic pop
  455. #endif
  456. DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
  457. DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
  458. DISTRHO_SAFE_ASSERT_RETURN(size < buffer->size, false);
  459. // empty
  460. if (buffer->head == buffer->tail)
  461. return false;
  462. uint8_t* const bytebuf(static_cast<uint8_t*>(buf));
  463. const uint32_t head(buffer->head);
  464. const uint32_t tail(buffer->tail);
  465. const uint32_t wrap((head > tail) ? 0 : buffer->size);
  466. if (size > wrap + head - tail)
  467. {
  468. if (! errorReading)
  469. {
  470. errorReading = true;
  471. d_stderr2("RingBuffer::tryRead(%p, %lu): failed, not enough space", buf, (ulong)size);
  472. }
  473. return false;
  474. }
  475. uint32_t readto(tail + size);
  476. if (readto > buffer->size)
  477. {
  478. readto -= buffer->size;
  479. if (size == 1)
  480. {
  481. std::memcpy(bytebuf, buffer->buf + tail, 1);
  482. }
  483. else
  484. {
  485. const uint32_t firstpart(buffer->size - tail);
  486. std::memcpy(bytebuf, buffer->buf + tail, firstpart);
  487. std::memcpy(bytebuf + firstpart, buffer->buf, readto);
  488. }
  489. }
  490. else
  491. {
  492. std::memcpy(bytebuf, buffer->buf + tail, size);
  493. if (readto == buffer->size)
  494. readto = 0;
  495. }
  496. buffer->tail = readto;
  497. errorReading = false;
  498. return true;
  499. }
  500. /** @internal try writing to the buffer, can fail. */
  501. bool tryWrite(const void* const buf, const uint32_t size) noexcept
  502. {
  503. DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
  504. DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
  505. DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
  506. DISTRHO_SAFE_ASSERT_UINT2_RETURN(size < buffer->size, size, buffer->size, false);
  507. const uint8_t* const bytebuf(static_cast<const uint8_t*>(buf));
  508. const uint32_t tail(buffer->tail);
  509. const uint32_t wrtn(buffer->wrtn);
  510. const uint32_t wrap((tail > wrtn) ? 0 : buffer->size);
  511. if (size >= wrap + tail - wrtn)
  512. {
  513. if (! errorWriting)
  514. {
  515. errorWriting = true;
  516. d_stderr2("RingBuffer::tryWrite(%p, %lu): failed, not enough space", buf, (ulong)size);
  517. }
  518. buffer->invalidateCommit = true;
  519. return false;
  520. }
  521. uint32_t writeto(wrtn + size);
  522. if (writeto > buffer->size)
  523. {
  524. writeto -= buffer->size;
  525. if (size == 1)
  526. {
  527. std::memcpy(buffer->buf, bytebuf, 1);
  528. }
  529. else
  530. {
  531. const uint32_t firstpart(buffer->size - wrtn);
  532. std::memcpy(buffer->buf + wrtn, bytebuf, firstpart);
  533. std::memcpy(buffer->buf, bytebuf + firstpart, writeto);
  534. }
  535. }
  536. else
  537. {
  538. std::memcpy(buffer->buf + wrtn, bytebuf, size);
  539. if (writeto == buffer->size)
  540. writeto = 0;
  541. }
  542. buffer->wrtn = writeto;
  543. return true;
  544. }
  545. private:
  546. /** Buffer struct pointer. */
  547. BufferStruct* buffer;
  548. /** Whether read errors have been printed to terminal. */
  549. bool errorReading;
  550. /** Whether write errors have been printed to terminal. */
  551. bool errorWriting;
  552. DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
  553. DISTRHO_DECLARE_NON_COPYABLE(RingBufferControl)
  554. };
  555. template <class BufferStruct>
  556. inline bool RingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
  557. {
  558. return (buffer != nullptr && buffer->head != buffer->tail);
  559. }
  560. template <>
  561. inline bool RingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
  562. {
  563. return (buffer != nullptr && buffer->buf != nullptr && buffer->head != buffer->tail);
  564. }
  565. // -----------------------------------------------------------------------
  566. // RingBuffer using heap space
  567. /**
  568. RingBufferControl with a heap buffer.
  569. This is a convenience class that provides a method for creating and destroying the heap data.
  570. Requires the use of createBuffer(uint32_t) to make the ring buffer usable.
  571. */
  572. class HeapRingBuffer : public RingBufferControl<HeapBuffer>
  573. {
  574. public:
  575. /** Constructor. */
  576. HeapRingBuffer() noexcept
  577. : heapBuffer(HeapBuffer_INIT)
  578. {
  579. #ifndef DISTRHO_PROPER_CPP11_SUPPORT
  580. std::memset(&heapBuffer, 0, sizeof(heapBuffer));
  581. #endif
  582. }
  583. /** Destructor. */
  584. ~HeapRingBuffer() noexcept override
  585. {
  586. if (heapBuffer.buf == nullptr)
  587. return;
  588. delete[] heapBuffer.buf;
  589. heapBuffer.buf = nullptr;
  590. }
  591. /** Create a buffer of the specified size. */
  592. bool createBuffer(const uint32_t size) noexcept
  593. {
  594. DISTRHO_SAFE_ASSERT_RETURN(heapBuffer.buf == nullptr, false);
  595. DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
  596. const uint32_t p2size = d_nextPowerOf2(size);
  597. try {
  598. heapBuffer.buf = new uint8_t[p2size];
  599. } DISTRHO_SAFE_EXCEPTION_RETURN("HeapRingBuffer::createBuffer", false);
  600. heapBuffer.size = p2size;
  601. setRingBuffer(&heapBuffer, true);
  602. return true;
  603. }
  604. /** Delete the previously allocated buffer. */
  605. void deleteBuffer() noexcept
  606. {
  607. DISTRHO_SAFE_ASSERT_RETURN(heapBuffer.buf != nullptr,);
  608. setRingBuffer(nullptr, false);
  609. delete[] heapBuffer.buf;
  610. heapBuffer.buf = nullptr;
  611. heapBuffer.size = 0;
  612. }
  613. private:
  614. /** The heap buffer used for this class. */
  615. HeapBuffer heapBuffer;
  616. DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
  617. DISTRHO_DECLARE_NON_COPYABLE(HeapRingBuffer)
  618. };
  619. // -----------------------------------------------------------------------
  620. // RingBuffer using small stack space
  621. /**
  622. RingBufferControl with an included small stack buffer.
  623. No setup is necessary, this class is usable as-is.
  624. */
  625. class SmallStackRingBuffer : public RingBufferControl<SmallStackBuffer>
  626. {
  627. public:
  628. /** Constructor. */
  629. SmallStackRingBuffer() noexcept
  630. : stackBuffer(StackBuffer_INIT)
  631. {
  632. #ifndef DISTRHO_PROPER_CPP11_SUPPORT
  633. std::memset(&stackBuffer, 0, sizeof(stackBuffer));
  634. #endif
  635. setRingBuffer(&stackBuffer, true);
  636. }
  637. private:
  638. /** The small stack buffer used for this class. */
  639. SmallStackBuffer stackBuffer;
  640. DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
  641. DISTRHO_DECLARE_NON_COPYABLE(SmallStackRingBuffer)
  642. };
  643. // -----------------------------------------------------------------------
  644. END_NAMESPACE_DISTRHO
  645. #endif // DISTRHO_RING_BUFFER_HPP_INCLUDED