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.

204 lines
4.4KB

  1. #pragma once
  2. #include <dsp/common.hpp>
  3. #include <string.h>
  4. namespace rack {
  5. namespace dsp {
  6. /** A simple cyclic buffer.
  7. S must be a power of 2.
  8. Thread-safe for single producers and consumers.
  9. */
  10. template <typename T, size_t S>
  11. struct RingBuffer {
  12. T data[S];
  13. size_t start = 0;
  14. size_t end = 0;
  15. size_t mask(size_t i) const {
  16. return i & (S - 1);
  17. }
  18. void push(T t) {
  19. size_t i = mask(end++);
  20. data[i] = t;
  21. }
  22. void pushBuffer(const T* t, int n) {
  23. size_t i = mask(end);
  24. size_t e1 = i + n;
  25. size_t e2 = (e1 < S) ? e1 : S;
  26. std::memcpy(&data[i], t, sizeof(T) * (e2 - i));
  27. if (e1 > S) {
  28. std::memcpy(data, &t[S - i], sizeof(T) * (e1 - S));
  29. }
  30. end += n;
  31. }
  32. T shift() {
  33. return data[mask(start++)];
  34. }
  35. void shiftBuffer(T* t, size_t n) {
  36. size_t i = mask(start);
  37. size_t s1 = i + n;
  38. size_t s2 = (s1 < S) ? s1 : S;
  39. std::memcpy(t, &data[i], sizeof(T) * (s2 - i));
  40. if (s1 > S) {
  41. std::memcpy(&t[S - i], data, sizeof(T) * (s1 - S));
  42. }
  43. start += n;
  44. }
  45. void clear() {
  46. start = end;
  47. }
  48. bool empty() const {
  49. return start == end;
  50. }
  51. bool full() const {
  52. return end - start == S;
  53. }
  54. size_t size() const {
  55. return end - start;
  56. }
  57. size_t capacity() const {
  58. return S - size();
  59. }
  60. };
  61. /** A cyclic buffer which maintains a valid linear array of size S by keeping a copy of the buffer in adjacent memory.
  62. S must be a power of 2.
  63. Thread-safe for single producers and consumers?
  64. */
  65. template <typename T, size_t S>
  66. struct DoubleRingBuffer {
  67. T data[S * 2];
  68. size_t start = 0;
  69. size_t end = 0;
  70. size_t mask(size_t i) const {
  71. return i & (S - 1);
  72. }
  73. void push(T t) {
  74. size_t i = mask(end++);
  75. data[i] = t;
  76. data[i + S] = t;
  77. }
  78. T shift() {
  79. return data[mask(start++)];
  80. }
  81. void clear() {
  82. start = end;
  83. }
  84. bool empty() const {
  85. return start == end;
  86. }
  87. bool full() const {
  88. return end - start == S;
  89. }
  90. size_t size() const {
  91. return end - start;
  92. }
  93. size_t capacity() const {
  94. return S - size();
  95. }
  96. /** Returns a pointer to S consecutive elements for appending.
  97. If any data is appended, you must call endIncr afterwards.
  98. Pointer is invalidated when any other method is called.
  99. */
  100. T* endData() {
  101. return &data[mask(end)];
  102. }
  103. void endIncr(size_t n) {
  104. size_t e = mask(end);
  105. size_t e1 = e + n;
  106. size_t e2 = (e1 < S) ? e1 : S;
  107. // Copy data forward
  108. std::memcpy(&data[S + e], &data[e], sizeof(T) * (e2 - e));
  109. if (e1 > S) {
  110. // Copy data backward from the doubled block to the main block
  111. std::memcpy(data, &data[S], sizeof(T) * (e1 - S));
  112. }
  113. end += n;
  114. }
  115. /** Returns a pointer to S consecutive elements for consumption
  116. If any data is consumed, call startIncr afterwards.
  117. */
  118. const T* startData() const {
  119. return &data[mask(start)];
  120. }
  121. void startIncr(size_t n) {
  122. start += n;
  123. }
  124. };
  125. /** A cyclic buffer which maintains a valid linear array of size S by sliding along a larger block of size N.
  126. The linear array of S elements are moved back to the start of the block once it outgrows past the end.
  127. This happens every N - S pushes, so the push() time is O(1 + S / (N - S)).
  128. For example, a float buffer of size 64 in a block of size 1024 is nearly as efficient as RingBuffer.
  129. Not thread-safe.
  130. */
  131. template <typename T, size_t S, size_t N>
  132. struct AppleRingBuffer {
  133. T data[N];
  134. size_t start = 0;
  135. size_t end = 0;
  136. void returnBuffer() {
  137. // move end block to beginning
  138. // may overlap, but memmove handles that correctly
  139. size_t s = size();
  140. std::memmove(data, &data[start], sizeof(T) * s);
  141. start = 0;
  142. end = s;
  143. }
  144. void push(T t) {
  145. if (end + 1 > N) {
  146. returnBuffer();
  147. }
  148. data[end++] = t;
  149. }
  150. T shift() {
  151. return data[start++];
  152. }
  153. bool empty() const {
  154. return start == end;
  155. }
  156. bool full() const {
  157. return end - start == S;
  158. }
  159. size_t size() const {
  160. return end - start;
  161. }
  162. size_t capacity() const {
  163. return S - size();
  164. }
  165. /** Returns a pointer to S consecutive elements for appending, requesting to append n elements.
  166. */
  167. T* endData(size_t n) {
  168. if (end + n > N) {
  169. returnBuffer();
  170. }
  171. return &data[end];
  172. }
  173. /** Actually increments the end position
  174. Must be called after endData(), and `n` must be at most the `n` passed to endData()
  175. */
  176. void endIncr(size_t n) {
  177. end += n;
  178. }
  179. /** Returns a pointer to S consecutive elements for consumption
  180. If any data is consumed, call startIncr afterwards.
  181. */
  182. const T* startData() const {
  183. return &data[start];
  184. }
  185. void startIncr(size_t n) {
  186. // This is valid as long as n < S
  187. start += n;
  188. }
  189. };
  190. } // namespace dsp
  191. } // namespace rack