| @@ -13,7 +13,7 @@ This is all there is to it: · 12 unmodified lines |
| 13 | 13 | |
| 14 | 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 | 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 | 26 | positive, the exponential curve will get more concave, with |
| 27 | 27 | more values staying closer to 0. |
| 28 | 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 | 59 | * Naive Function (stateless). |
| 30 | 60 | The function =sk_expmap_stateless= will compute |
| 31 | 61 | this exponential function without any internal state. |
| @@ -59,22 +89,161 @@ struct sk_expmap { · 27 unmodified lines |
| 59 | 89 | <<sk_expmap>> |
| 60 | 90 | }; |
| 61 | 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 | 100 | #+NAME: sk_expmap |
| 64 | 101 | #+BEGIN_SRC c |
| 65 | | −SKFLT prev; |
| 66 | | −SKFLT slope; |
| 67 | | −SKFLT pslope; |
| 68 | | −SKFLT scale; |
| 102 | +SKFLT pin; |
| 69 | 103 | #+END_SRC |
| 70 | 104 | |
| 71 | | −=prev= is the previous input value, will be used to avoid |
| 72 | | −computation. |
| 105 | +#+NAME: init |
| 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 |
| 75 | | −slope parameter. When they don't match, =scale= is updated. |
| 114 | +#+NAME: sk_expmap |
| 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 | 124 | =scale= is the constant =1/(1 - exp(slope)=. A scaler used |
| 78 | 125 | to shave off an expensive =exp= operation and =div= |
| 79 | 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 | 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 |