| @@ -13,7 +13,7 @@ This is all there is to it: · 12 unmodified lines |
| 13 | | 13 | |
| 14 | @!(fig "expmap_eqn" | 14 | @!(fig "expmap_eqn" |
| 15 | " | 15 | " |
| 16 | {1 - e^{x*s} \\over 1 - e^{s}} | 16 | {1 - e^{xs} \\over 1 - e^{s}} |
| 17 | ")!@ | 17 | ")!@ |
| 18 | | 18 | |
| 19 | Where @!(smallfig "expmap_x" "x")!@ is the normalized | 19 | Where @!(smallfig "expmap_x" "x")!@ is the normalized |
| @@ -26,6 +26,36 @@ staying closer to 1. As @!(smallfig "expmap_s")!@ gets more · 6 unmodified lines |
| 26 | positive, the exponential curve will get more concave, with | 26 | positive, the exponential curve will get more concave, with |
| 27 | more values staying closer to 0. | 27 | more values staying closer to 0. |
| 28 | * Tangled Files | 28 | * Tangled Files |
| | 29 | =expmap.c= and =expmap.h= are the tangled files. If |
| | 30 | =SK_EXPMAP_PRIV= is defined, it exposes the structs. |
| | 31 | |
| | 32 | #+NAME: expmap.c |
| | 33 | #+BEGIN_SRC c :tangle expmap.c |
| | 34 | #include <math.h> |
| | 35 | #define SK_EXPMAP_PRIV |
| | 36 | #include "expmap.h" |
| | 37 | <<static_funcdefs>> |
| | 38 | <<funcs>> |
| | 39 | #+END_SRC |
| | 40 | |
| | 41 | #+NAME: expmap.h |
| | 42 | #+BEGIN_SRC c :tangle expmap.h |
| | 43 | #ifndef SK_EXPMAP_H |
| | 44 | #define SK_EXPMAP_H |
| | 45 | |
| | 46 | #ifndef SKFLT |
| | 47 | #define SKFLT float |
| | 48 | #endif |
| | 49 | |
| | 50 | <<typedefs>> |
| | 51 | <<funcdefs>> |
| | 52 | |
| | 53 | #ifdef SK_EXPMAP_PRIV |
| | 54 | <<structs>> |
| | 55 | #endif |
| | 56 | |
| | 57 | #endif |
| | 58 | #+END_SRC |
| 29 | * Naive Function (stateless). | 59 | * Naive Function (stateless). |
| 30 | The function =sk_expmap_stateless= will compute | 60 | The function =sk_expmap_stateless= will compute |
| 31 | this exponential function without any internal state. | 61 | this exponential function without any internal state. |
| @@ -59,22 +89,161 @@ struct sk_expmap { · 27 unmodified lines |
| 59 | <<sk_expmap>> | 89 | <<sk_expmap>> |
| 60 | }; | 90 | }; |
| 61 | #+END_SRC | 91 | #+END_SRC |
| | 92 | * Input/Ouput Caching |
| | 93 | To avoid computation, some caching of the input/output |
| | 94 | variables are used. |
| | 95 | |
| | 96 | =pin= is the cached input value, will be used to avoid |
| | 97 | computation. At init, it is set to be an (invalid) negative |
| | 98 | value. |
| 62 | | 99 | |
| 63 | #+NAME: sk_expmap | 100 | #+NAME: sk_expmap |
| 64 | #+BEGIN_SRC c | 101 | #+BEGIN_SRC c |
| 65 | SKFLT prev; | 102 | SKFLT pin; |
| 66 | SKFLT slope; | | |
| 67 | SKFLT pslope; | | |
| 68 | SKFLT scale; | | |
| 69 | #+END_SRC | 103 | #+END_SRC |
| 70 | | 104 | |
| 71 | =prev= is the previous input value, will be used to avoid | 105 | #+NAME: init |
| 72 | computation. | 106 | #+BEGIN_SRC c |
| | 107 | em->pin = -1; |
| | 108 | #+END_SRC |
| | 109 | |
| | 110 | =pout= is the cached computed output value. It is used if no |
| | 111 | computation is needed. By default, it is set to be -1. It is |
| | 112 | assumed that this value shouldn't actually ever be returned. |
| 73 | | 113 | |
| 74 | =slope= and =pslope= is the slope parameter and previous | 114 | #+NAME: sk_expmap |
| 75 | slope parameter. When they don't match, =scale= is updated. | 115 | #+BEGIN_SRC c |
| | 116 | SKFLT pout; |
| | 117 | #+END_SRC |
| 76 | | 118 | |
| | 119 | #+NAME: init |
| | 120 | #+BEGIN_SRC c |
| | 121 | em->pout = -1; |
| | 122 | #+END_SRC |
| | 123 | * Scaling Constant |
| 77 | =scale= is the constant =1/(1 - exp(slope)=. A scaler used | 124 | =scale= is the constant =1/(1 - exp(slope)=. A scaler used |
| 78 | to shave off an expensive =exp= operation and =div= | 125 | to shave off an expensive =exp= operation and =div= |
| 79 | operation. | 126 | operation. |
| | 127 | |
| | 128 | #+NAME: sk_expmap |
| | 129 | #+BEGIN_SRC c |
| | 130 | SKFLT scale; |
| | 131 | #+END_SRC |
| | 132 | |
| | 133 | The scaling value can be computed with the static function |
| | 134 | =compute_scale=, given an input slope. |
| | 135 | |
| | 136 | #+NAME: static_funcdefs |
| | 137 | #+BEGIN_SRC c |
| | 138 | static SKFLT compute_scale(SKFLT slope); |
| | 139 | #+END_SRC |
| | 140 | |
| | 141 | #+NAME: funcs |
| | 142 | #+BEGIN_SRC c |
| | 143 | static SKFLT compute_scale(SKFLT slope) |
| | 144 | { |
| | 145 | return 1 / (1 - exp(slope)); |
| | 146 | } |
| | 147 | #+END_SRC |
| | 148 | |
| | 149 | After the =slope= is initialized, the scale is initialized |
| | 150 | to be the slope. |
| | 151 | |
| | 152 | #+NAME: init_scale |
| | 153 | #+BEGIN_SRC c |
| | 154 | em->scale = compute_scale(em->slope); |
| | 155 | #+END_SRC |
| | 156 | * Initialization |
| | 157 | Initialization is done with =sk_expmap_init=. |
| | 158 | |
| | 159 | #+NAME: funcdefs |
| | 160 | #+BEGIN_SRC c |
| | 161 | void sk_expmap_init(sk_expmap *em); |
| | 162 | #+END_SRC |
| | 163 | |
| | 164 | #+NAME: funcs |
| | 165 | #+BEGIN_SRC c |
| | 166 | void sk_expmap_init(sk_expmap *em) |
| | 167 | { |
| | 168 | <<init>> |
| | 169 | } |
| | 170 | #+END_SRC |
| | 171 | * Changing Slope |
| | 172 | The slope of expmap can be changed with =sk_expmap_slope=. |
| | 173 | |
| | 174 | #+NAME: funcdefs |
| | 175 | #+BEGIN_SRC c |
| | 176 | void sk_expmap_slope(sk_expmap *em, SKFLT slope); |
| | 177 | #+END_SRC |
| | 178 | |
| | 179 | #+NAME: funcs |
| | 180 | #+BEGIN_SRC c |
| | 181 | void sk_expmap_slope(sk_expmap *em, SKFLT slope) |
| | 182 | { |
| | 183 | em->slope = slope; |
| | 184 | } |
| | 185 | #+END_SRC |
| | 186 | |
| | 187 | The slope parameter uses caching to avoid re-computation. |
| | 188 | |
| | 189 | #+NAME: sk_expmap |
| | 190 | #+BEGIN_SRC c |
| | 191 | SKFLT slope; |
| | 192 | SKFLT pslope; |
| | 193 | #+END_SRC |
| | 194 | |
| | 195 | The slope is set to be =1= to begin. |
| | 196 | |
| | 197 | #+NAME: init |
| | 198 | #+BEGIN_SRC c |
| | 199 | sk_expmap_slope(em, 1); |
| | 200 | em->pslope = 1; |
| | 201 | <<init_scale>> |
| | 202 | #+END_SRC |
| 80 | * Compute | 203 | * Compute |
| | 204 | A single sample of audio is computed with =sk_expmap_tick=. |
| | 205 | |
| | 206 | #+NAME: funcdefs |
| | 207 | #+BEGIN_SRC c |
| | 208 | SKFLT sk_expmap_tick(sk_expmap *em, SKFLT in); |
| | 209 | #+END_SRC |
| | 210 | |
| | 211 | #+NAME: funcs |
| | 212 | #+BEGIN_SRC c |
| | 213 | SKFLT sk_expmap_tick(sk_expmap *em, SKFLT in) |
| | 214 | { |
| | 215 | SKFLT out = 0; |
| | 216 | <<update_scale_value>> |
| | 217 | <<compute>> |
| | 218 | return out; |
| | 219 | } |
| | 220 | #+END_SRC |
| | 221 | |
| | 222 | Before a sample is computed, the slope parameter is checked |
| | 223 | for updates. If it is updated, the scale needs to be |
| | 224 | re-computed. |
| | 225 | |
| | 226 | #+NAME: update_scale_value |
| | 227 | #+BEGIN_SRC c |
| | 228 | if (em->slope != em->pslope) { |
| | 229 | em->pslope = em->slope; |
| | 230 | em->scale = compute_scale(em->slope); |
| | 231 | } |
| | 232 | #+END_SRC |
| | 233 | |
| | 234 | Computation is done only if the input value =in= is |
| | 235 | different from the cached input value =pin=. If |
| | 236 | it is, it updates the cached output value =pout=. |
| | 237 | |
| | 238 | |
| | 239 | the output variable =out= is set to be the cached value |
| | 240 | =pout=. |
| | 241 | |
| | 242 | #+NAME: compute |
| | 243 | #+BEGIN_SRC c |
| | 244 | if (in != em->pin) { |
| | 245 | em->pin = in; |
| | 246 | em->pout = (1 - exp(in * em->slope)) * em->scale; |
| | 247 | } |
| | 248 | out = em->pout; |
| | 249 | #+END_SRC |