| @@ -0,0 +1,152 @@ |
| | 1 | #+TITLE: Biramp |
| | 2 | * Overview |
| | 3 | Produces an up-and-down ramp signal from an input phasor |
| | 4 | ramp signal. Things like this are useful for creating quick |
| | 5 | time-synced LFO signals. Because it ramps down to 0, it |
| | 6 | won't clip. |
| | 7 | |
| | 8 | A few flavors of biramp will be created: a stateless |
| | 9 | version with position control, a stateful version with |
| | 10 | position that only updates at the beginnig of the phasor, |
| | 11 | and a stateful version with no position contorl that |
| | 12 | inverts the direction of the phasor at the beginning of |
| | 13 | each phase. |
| | 14 | * Tangled Files |
| | 15 | Tangles into =biramp.c= and =biramp.h=. |
| | 16 | |
| | 17 | #+NAME: biramp.h |
| | 18 | #+BEGIN_SRC c :tangle biramp.h |
| | 19 | #ifndef SK_BIRAMP_H |
| | 20 | #define SK_BIRAMP_H |
| | 21 | |
| | 22 | #ifndef SKFLT |
| | 23 | #define SKFLT float |
| | 24 | #endif |
| | 25 | |
| | 26 | <<funcdefs>> |
| | 27 | #endif |
| | 28 | #+END_SRC |
| | 29 | |
| | 30 | #+NAME: biramp.c |
| | 31 | #+BEGIN_SRC c :tangle biramp.c |
| | 32 | #define SK_BIRAMP_PRIV |
| | 33 | #include "biramp.h" |
| | 34 | <<funcs>> |
| | 35 | #+END_SRC |
| | 36 | * Version 1: Stateless |
| | 37 | The stateless version of biramp is called |
| | 38 | =sk_biramp_stateless=. |
| | 39 | |
| | 40 | #+NAME: funcdefs |
| | 41 | #+BEGIN_SRC c |
| | 42 | SKFLT sk_biramp_stateless(SKFLT phs, SKFLT pos); |
| | 43 | #+END_SRC |
| | 44 | |
| | 45 | The stateless version needs only the input phasor signal |
| | 46 | and split position (expected to be a normalized value). |
| | 47 | |
| | 48 | When the phasor is less than the position, the output is |
| | 49 | =phasor/position=. |
| | 50 | |
| | 51 | When the phasor is greater than or equal to the position, |
| | 52 | the output is =1 - phasor/1 - position=. |
| | 53 | |
| | 54 | The position cannot be 0 or 1 exactly. Instead of throwing |
| | 55 | an error, these will just return themselves. |
| | 56 | |
| | 57 | #+NAME: funcs |
| | 58 | #+BEGIN_SRC c |
| | 59 | SKFLT sk_biramp_stateless(SKFLT phs, SKFLT pos) |
| | 60 | { |
| | 61 | SKFLT out; |
| | 62 | out = 0; |
| | 63 | |
| | 64 | if (pos == 0 || pos == 1) return pos; |
| | 65 | |
| | 66 | if (phs < pos) out = phs / pos; |
| | 67 | else out = (1 - phs) / (1 - pos); |
| | 68 | |
| | 69 | return out; |
| | 70 | } |
| | 71 | #+END_SRC |
| | 72 | * Version 2: Stateful |
| | 73 | The function =sk_biramp_stateful= is a variation of the |
| | 74 | stateless biramp with some managed internal state. |
| | 75 | |
| | 76 | #+NAME: funcdefs |
| | 77 | #+BEGIN_SRC c |
| | 78 | SKFLT sk_biramp_stateful(SKFLT phs, SKFLT pos, |
| | 79 | SKFLT *lphs, SKFLT *lpos); |
| | 80 | #+END_SRC |
| | 81 | |
| | 82 | The stateful version of this functionality can be used so |
| | 83 | that the position only updates when the phasor resets back |
| | 84 | to zero. To do this, two additional pointer values are |
| | 85 | given: the previous phase, and the used cached position |
| | 86 | parameter. |
| | 87 | |
| | 88 | This will use the cached position value instead of the |
| | 89 | immediation position value to compute the biramp. The |
| | 90 | cached value will update itself if it is less than 0, or if |
| | 91 | it finds the phasor has reset. The phasor resets when the |
| | 92 | last phasor value is greater than the current phasor value. |
| | 93 | |
| | 94 | The cached phasor value is updated here as well. |
| | 95 | |
| | 96 | #+NAME: funcs |
| | 97 | #+BEGIN_SRC c |
| | 98 | SKFLT sk_biramp_stateful(SKFLT phs, SKFLT pos, |
| | 99 | SKFLT *lphs, SKFLT *lpos) |
| | 100 | { |
| | 101 | SKFLT out; |
| | 102 | out = 0; |
| | 103 | |
| | 104 | if (*lphs > phs || *lpos < 0) { |
| | 105 | *lpos = pos; |
| | 106 | } |
| | 107 | |
| | 108 | out = sk_biramp_stateless(phs, *lpos); |
| | 109 | |
| | 110 | *lphs = phs; |
| | 111 | return out; |
| | 112 | } |
| | 113 | #+END_SRC |
| | 114 | * Version 3: Flipper |
| | 115 | The final variation, called a =flipper=, will |
| | 116 | produce a phasor signal whose direction is flipped when the |
| | 117 | phase is reset. The stateful variables here include the |
| | 118 | cached phasor signal from the previous signal, and an |
| | 119 | integer storing the direction. When the direction is 0, it |
| | 120 | is normal. Non-zero is a flip. |
| | 121 | |
| | 122 | The direction state is updated on a phase reset, when the |
| | 123 | cached phasor is greater than the current phasor. |
| | 124 | |
| | 125 | It's important that the cached last phase value is stored |
| | 126 | pre-flipped. That way, the phasor consistently stays in one |
| | 127 | direction and makes the reset checker very straight-forward. |
| | 128 | |
| | 129 | #+NAME: funcdefs |
| | 130 | #+BEGIN_SRC c |
| | 131 | SKFLT sk_biramp_flipper(SKFLT phs, SKFLT *lphs, int *flipit); |
| | 132 | #+END_SRC |
| | 133 | |
| | 134 | #+NAME: funcs |
| | 135 | #+BEGIN_SRC c |
| | 136 | SKFLT sk_biramp_flipper(SKFLT phs, SKFLT *lphs, int *flipit) |
| | 137 | { |
| | 138 | SKFLT out; |
| | 139 | out = 0; |
| | 140 | |
| | 141 | if (*lphs > phs) { |
| | 142 | (*flipit) = ~(*flipit); |
| | 143 | } |
| | 144 | |
| | 145 | out = phs; |
| | 146 | *lphs = phs; |
| | 147 | |
| | 148 | if (*flipit) out = 1 - out; |
| | 149 | |
| | 150 | return out; |
| | 151 | } |
| | 152 | #+END_SRC |