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.

258 lines
4.7KB

  1. #pragma once
  2. #include <dsp/common.hpp>
  3. namespace rack {
  4. namespace dsp {
  5. /** Detects when a boolean changes from false to true */
  6. struct BooleanTrigger {
  7. enum State : uint8_t {
  8. LOW,
  9. HIGH,
  10. UNINITIALIZED
  11. };
  12. union {
  13. State s = UNINITIALIZED;
  14. /** Deprecated */
  15. bool state;
  16. };
  17. void reset() {
  18. s = UNINITIALIZED;
  19. }
  20. /** Returns whether the input changed from false to true. */
  21. bool process(bool in) {
  22. bool triggered = (s == LOW) && in;
  23. s = in ? HIGH : LOW;
  24. return triggered;
  25. }
  26. enum Event {
  27. NONE = 0,
  28. TRIGGERED = 1,
  29. UNTRIGGERED = -1
  30. };
  31. /** Returns TRIGGERED if the input changed from false to true, and UNTRIGGERED if the input changed from true to false.
  32. */
  33. Event processEvent(bool in) {
  34. Event event = NONE;
  35. if (s == LOW && in) {
  36. event = TRIGGERED;
  37. }
  38. else if (s == HIGH && !in) {
  39. event = UNTRIGGERED;
  40. }
  41. s = in ? HIGH : LOW;
  42. return event;
  43. }
  44. bool isHigh() {
  45. return s == HIGH;
  46. }
  47. };
  48. /** Turns HIGH when value reaches a threshold (default 0.f), turns LOW when value reaches a threshold (default 1.f).
  49. */
  50. template <typename T = float>
  51. struct TSchmittTrigger {
  52. T state;
  53. TSchmittTrigger() {
  54. reset();
  55. }
  56. void reset() {
  57. state = T::mask();
  58. }
  59. T process(T in, T lowThreshold = 0.f, T highThreshold = 1.f) {
  60. T on = (in >= highThreshold);
  61. T off = (in <= lowThreshold);
  62. T triggered = ~state & on;
  63. state = on | (state & ~off);
  64. return triggered;
  65. }
  66. T isHigh() {
  67. return state;
  68. }
  69. };
  70. template <>
  71. struct TSchmittTrigger<float> {
  72. enum State : uint8_t {
  73. LOW,
  74. HIGH,
  75. UNINITIALIZED
  76. };
  77. union {
  78. State s = UNINITIALIZED;
  79. /** Deprecated. Backward compatible API */
  80. bool state;
  81. };
  82. void reset() {
  83. s = UNINITIALIZED;
  84. }
  85. /** Updates the state of the Schmitt Trigger given a value.
  86. Returns true if triggered, i.e. the value increases from 0 to 1.
  87. If different trigger thresholds are needed, use
  88. process(in, 0.1f, 2.f)
  89. for example.
  90. */
  91. bool process(float in, float lowThreshold = 0.f, float highThreshold = 1.f) {
  92. if (s == LOW && in >= highThreshold) {
  93. // LOW to HIGH
  94. s = HIGH;
  95. return true;
  96. }
  97. else if (s == HIGH && in <= lowThreshold) {
  98. // HIGH to LOW
  99. s = LOW;
  100. }
  101. else if (s == UNINITIALIZED && in >= highThreshold) {
  102. // UNINITIALIZED to HIGH
  103. s = HIGH;
  104. }
  105. else if (s == UNINITIALIZED && in <= lowThreshold) {
  106. // UNINITIALIZED to LOW
  107. s = LOW;
  108. }
  109. return false;
  110. }
  111. enum Event {
  112. NONE = 0,
  113. TRIGGERED = 1,
  114. UNTRIGGERED = -1
  115. };
  116. /** Returns TRIGGERED if the input reached highThreshold, and UNTRIGGERED if the input reached lowThreshold.
  117. */
  118. Event processEvent(float in, float lowThreshold = 0.f, float highThreshold = 1.f) {
  119. Event event = NONE;
  120. if (s == LOW && in >= highThreshold) {
  121. // LOW to HIGH
  122. s = HIGH;
  123. event = TRIGGERED;
  124. }
  125. else if (s == HIGH && in <= lowThreshold) {
  126. // HIGH to LOW
  127. s = LOW;
  128. event = UNTRIGGERED;
  129. }
  130. else if (s == UNINITIALIZED && in >= highThreshold) {
  131. // UNINITIALIZED to HIGH
  132. s = HIGH;
  133. }
  134. else if (s == UNINITIALIZED && in <= lowThreshold) {
  135. // UNINITIALIZED to LOW
  136. s = LOW;
  137. }
  138. return event;
  139. }
  140. bool isHigh() {
  141. return s == HIGH;
  142. }
  143. };
  144. typedef TSchmittTrigger<> SchmittTrigger;
  145. /** When triggered, holds a high value for a specified time before going low again */
  146. struct PulseGenerator {
  147. float remaining = 0.f;
  148. /** Immediately disables the pulse */
  149. void reset() {
  150. remaining = 0.f;
  151. }
  152. /** Advances the state by `deltaTime`. Returns whether the pulse is in the HIGH state. */
  153. bool process(float deltaTime) {
  154. if (remaining > 0.f) {
  155. remaining -= deltaTime;
  156. return true;
  157. }
  158. return false;
  159. }
  160. /** Begins a trigger with the given `duration`. */
  161. void trigger(float duration = 1e-3f) {
  162. // Keep the previous pulse if the existing pulse will be held longer than the currently requested one.
  163. if (duration > remaining) {
  164. remaining = duration;
  165. }
  166. }
  167. };
  168. /** Accumulates a timer when process() is called. */
  169. template <typename T = float>
  170. struct TTimer {
  171. T time = 0.f;
  172. void reset() {
  173. time = 0.f;
  174. }
  175. /** Returns the time since last reset or initialization. */
  176. T process(T deltaTime) {
  177. time += deltaTime;
  178. return time;
  179. }
  180. T getTime() {
  181. return time;
  182. }
  183. };
  184. typedef TTimer<> Timer;
  185. /** Counts calls to process(), returning true every `division` calls.
  186. Example:
  187. if (divider.process()) {
  188. // Runs every `division` calls
  189. }
  190. */
  191. struct ClockDivider {
  192. uint32_t clock = 0;
  193. uint32_t division = 1;
  194. void reset() {
  195. clock = 0;
  196. }
  197. void setDivision(uint32_t division) {
  198. this->division = division;
  199. }
  200. uint32_t getDivision() {
  201. return division;
  202. }
  203. uint32_t getClock() {
  204. return clock;
  205. }
  206. /** Returns true when the clock reaches `division` and resets. */
  207. bool process() {
  208. clock++;
  209. if (clock >= division) {
  210. clock = 0;
  211. return true;
  212. }
  213. return false;
  214. }
  215. };
  216. } // namespace dsp
  217. } // namespace rack