Unified · Split
Binary file not shown.
| @@ -0,0 +1,11 @@ | ||
| 1 | +*.DS_Store | |
| 2 | +*.app | |
| 3 | +*.a | |
| 4 | +*.appex | |
| 5 | +*.vst | |
| 6 | +*.vst3 | |
| 7 | +*.component | |
| 8 | +*.xcuserstate | |
| 9 | +*.zip | |
| 10 | +*.swp | |
| 11 | +*/Builds | |
Binary file not shown.
| @@ -0,0 +1,423 @@ | ||
| 1 | +/* | |
| 2 | +** FFT and FHT routines | |
| 3 | +** Copyright 1988, 1993; Ron Mayer | |
| 4 | +** | |
| 5 | +** mayer_fht(fz,n); | |
| 6 | +** Does a hartley transform of "n" points in the array "fz". | |
| 7 | +** mayer_fft(n,real,imag) | |
| 8 | +** Does a fourier transform of "n" points of the "real" and | |
| 9 | +** "imag" arrays. | |
| 10 | +** mayer_ifft(n,real,imag) | |
| 11 | +** Does an inverse fourier transform of "n" points of the "real" | |
| 12 | +** and "imag" arrays. | |
| 13 | +** mayer_realfft(n,real) | |
| 14 | +** Does a real-valued fourier transform of "n" points of the | |
| 15 | +** "real" array. The real part of the transform ends | |
| 16 | +** up in the first half of the array and the imaginary part of the | |
| 17 | +** transform ends up in the second half of the array. | |
| 18 | +** mayer_realifft(n,real) | |
| 19 | +** The inverse of the realfft() routine above. | |
| 20 | +** | |
| 21 | +** | |
| 22 | +** NOTE: This routine uses at least 2 patented algorithms, and may be | |
| 23 | +** under the restrictions of a bunch of different organizations. | |
| 24 | +** Although I wrote it completely myself, it is kind of a derivative | |
| 25 | +** of a routine I once authored and released under the GPL, so it | |
| 26 | +** may fall under the free software foundation's restrictions; | |
| 27 | +** it was worked on as a Stanford Univ project, so they claim | |
| 28 | +** some rights to it; it was further optimized at work here, so | |
| 29 | +** I think this company claims parts of it. The patents are | |
| 30 | +** held by R. Bracewell (the FHT algorithm) and O. Buneman (the | |
| 31 | +** trig generator), both at Stanford Univ. | |
| 32 | +** If it were up to me, I'd say go do whatever you want with it; | |
| 33 | +** but it would be polite to give credit to the following people | |
| 34 | +** if you use this anywhere: | |
| 35 | +** Euler - probable inventor of the fourier transform. | |
| 36 | +** Gauss - probable inventor of the FFT. | |
| 37 | +** Hartley - probable inventor of the hartley transform. | |
| 38 | +** Buneman - for a really cool trig generator | |
| 39 | +** Mayer(me) - for authoring this particular version and | |
| 40 | +** including all the optimizations in one package. | |
| 41 | +** Thanks, | |
| 42 | +** Ron Mayer; mayer@acuson.com | |
| 43 | +** | |
| 44 | +*/ | |
| 45 | + | |
| 46 | +/* This is a slightly modified version of Mayer's contribution; write | |
| 47 | +* msp@ucsd.edu for the original code. Kudos to Mayer for a fine piece | |
| 48 | +* of work. -msp | |
| 49 | +*/ | |
| 50 | + | |
| 51 | +/* These pragmas are only used for MSVC, not MinGW or Cygwin <hans@at.or.at> */ | |
| 52 | +#ifdef _MSC_VER | |
| 53 | +#pragma warning( disable : 4305 ) /* uncast const double to float */ | |
| 54 | +#pragma warning( disable : 4244 ) /* uncast double to float */ | |
| 55 | +#pragma warning( disable : 4101 ) /* unused local variables */ | |
| 56 | +#endif | |
| 57 | + | |
| 58 | +/* the following is needed only to declare pd_fft() as exportable in MSW */ | |
| 59 | +#define t_sample float | |
| 60 | + | |
| 61 | +#define REAL t_sample | |
| 62 | +#define GOOD_TRIG | |
| 63 | + | |
| 64 | +#ifdef GOOD_TRIG | |
| 65 | +#else | |
| 66 | +#define FAST_TRIG | |
| 67 | +#endif | |
| 68 | + | |
| 69 | +#if defined(GOOD_TRIG) | |
| 70 | +#define FHT_SWAP(a,b,t) {(t)=(a);(a)=(b);(b)=(t);} | |
| 71 | +#define TRIG_VARS \ | |
| 72 | + int t_lam=0; | |
| 73 | +#define TRIG_INIT(k,c,s) \ | |
| 74 | + { \ | |
| 75 | + int i; \ | |
| 76 | + for (i=2 ; i<=k ; i++) \ | |
| 77 | + {coswrk[i]=costab[i];sinwrk[i]=sintab[i];} \ | |
| 78 | + t_lam = 0; \ | |
| 79 | + c = 1; \ | |
| 80 | + s = 0; \ | |
| 81 | + } | |
| 82 | +#define TRIG_NEXT(k,c,s) \ | |
| 83 | + { \ | |
| 84 | + int i,j; \ | |
| 85 | + (t_lam)++; \ | |
| 86 | + for (i=0 ; !((1<<i)&t_lam) ; i++); \ | |
| 87 | + i = k-i; \ | |
| 88 | + s = sinwrk[i]; \ | |
| 89 | + c = coswrk[i]; \ | |
| 90 | + if (i>1) \ | |
| 91 | + { \ | |
| 92 | + for (j=k-i+2 ; (1<<j)&t_lam ; j++); \ | |
| 93 | + j = k - j; \ | |
| 94 | + sinwrk[i] = halsec[i] * (sinwrk[i-1] + sinwrk[j]); \ | |
| 95 | + coswrk[i] = halsec[i] * (coswrk[i-1] + coswrk[j]); \ | |
| 96 | + } \ | |
| 97 | + } | |
| 98 | +#define TRIG_RESET(k,c,s) | |
| 99 | +#endif | |
| 100 | + | |
| 101 | +#if defined(FAST_TRIG) | |
| 102 | +#define TRIG_VARS \ | |
| 103 | + REAL t_c,t_s; | |
| 104 | +#define TRIG_INIT(k,c,s) \ | |
| 105 | + { \ | |
| 106 | + t_c = costab[k]; \ | |
| 107 | + t_s = sintab[k]; \ | |
| 108 | + c = 1; \ | |
| 109 | + s = 0; \ | |
| 110 | + } | |
| 111 | +#define TRIG_NEXT(k,c,s) \ | |
| 112 | + { \ | |
| 113 | + REAL t = c; \ | |
| 114 | + c = t*t_c - s*t_s; \ | |
| 115 | + s = t*t_s + s*t_c; \ | |
| 116 | + } | |
| 117 | +#define TRIG_RESET(k,c,s) | |
| 118 | +#endif | |
| 119 | + | |
| 120 | +static REAL halsec[20]= | |
| 121 | + { | |
| 122 | + 0, | |
| 123 | + 0, | |
| 124 | + .54119610014619698439972320536638942006107206337801, | |
| 125 | + .50979557910415916894193980398784391368261849190893, | |
| 126 | + .50241928618815570551167011928012092247859337193963, | |
| 127 | + .50060299823519630134550410676638239611758632599591, | |
| 128 | + .50015063602065098821477101271097658495974913010340, | |
| 129 | + .50003765191554772296778139077905492847503165398345, | |
| 130 | + .50000941253588775676512870469186533538523133757983, | |
| 131 | + .50000235310628608051401267171204408939326297376426, | |
| 132 | + .50000058827484117879868526730916804925780637276181, | |
| 133 | + .50000014706860214875463798283871198206179118093251, | |
| 134 | + .50000003676714377807315864400643020315103490883972, | |
| 135 | + .50000000919178552207366560348853455333939112569380, | |
| 136 | + .50000000229794635411562887767906868558991922348920, | |
| 137 | + .50000000057448658687873302235147272458812263401372 | |
| 138 | + }; | |
| 139 | +static REAL costab[20]= | |
| 140 | + { | |
| 141 | + .00000000000000000000000000000000000000000000000000, | |
| 142 | + .70710678118654752440084436210484903928483593768847, | |
| 143 | + .92387953251128675612818318939678828682241662586364, | |
| 144 | + .98078528040323044912618223613423903697393373089333, | |
| 145 | + .99518472667219688624483695310947992157547486872985, | |
| 146 | + .99879545620517239271477160475910069444320361470461, | |
| 147 | + .99969881869620422011576564966617219685006108125772, | |
| 148 | + .99992470183914454092164649119638322435060646880221, | |
| 149 | + .99998117528260114265699043772856771617391725094433, | |
| 150 | + .99999529380957617151158012570011989955298763362218, | |
| 151 | + .99999882345170190992902571017152601904826792288976, | |
| 152 | + .99999970586288221916022821773876567711626389934930, | |
| 153 | + .99999992646571785114473148070738785694820115568892, | |
| 154 | + .99999998161642929380834691540290971450507605124278, | |
| 155 | + .99999999540410731289097193313960614895889430318945, | |
| 156 | + .99999999885102682756267330779455410840053741619428 | |
| 157 | + }; | |
| 158 | +static REAL sintab[20]= | |
| 159 | + { | |
| 160 | + 1.0000000000000000000000000000000000000000000000000, | |
| 161 | + .70710678118654752440084436210484903928483593768846, | |
| 162 | + .38268343236508977172845998403039886676134456248561, | |
| 163 | + .19509032201612826784828486847702224092769161775195, | |
| 164 | + .09801714032956060199419556388864184586113667316749, | |
| 165 | + .04906767432741801425495497694268265831474536302574, | |
| 166 | + .02454122852291228803173452945928292506546611923944, | |
| 167 | + .01227153828571992607940826195100321214037231959176, | |
| 168 | + .00613588464915447535964023459037258091705788631738, | |
| 169 | + .00306795676296597627014536549091984251894461021344, | |
| 170 | + .00153398018628476561230369715026407907995486457522, | |
| 171 | + .00076699031874270452693856835794857664314091945205, | |
| 172 | + .00038349518757139558907246168118138126339502603495, | |
| 173 | + .00019174759731070330743990956198900093346887403385, | |
| 174 | + .00009587379909597734587051721097647635118706561284, | |
| 175 | + .00004793689960306688454900399049465887274686668768 | |
| 176 | + }; | |
| 177 | +static REAL coswrk[20]= | |
| 178 | + { | |
| 179 | + .00000000000000000000000000000000000000000000000000, | |
| 180 | + .70710678118654752440084436210484903928483593768847, | |
| 181 | + .92387953251128675612818318939678828682241662586364, | |
| 182 | + .98078528040323044912618223613423903697393373089333, | |
| 183 | + .99518472667219688624483695310947992157547486872985, | |
| 184 | + .99879545620517239271477160475910069444320361470461, | |
| 185 | + .99969881869620422011576564966617219685006108125772, | |
| 186 | + .99992470183914454092164649119638322435060646880221, | |
| 187 | + .99998117528260114265699043772856771617391725094433, | |
| 188 | + .99999529380957617151158012570011989955298763362218, | |
| 189 | + .99999882345170190992902571017152601904826792288976, | |
| 190 | + .99999970586288221916022821773876567711626389934930, | |
| 191 | + .99999992646571785114473148070738785694820115568892, | |
| 192 | + .99999998161642929380834691540290971450507605124278, | |
| 193 | + .99999999540410731289097193313960614895889430318945, | |
| 194 | + .99999999885102682756267330779455410840053741619428 | |
| 195 | + }; | |
| 196 | +static REAL sinwrk[20]= | |
| 197 | + { | |
| 198 | + 1.0000000000000000000000000000000000000000000000000, | |
| 199 | + .70710678118654752440084436210484903928483593768846, | |
| 200 | + .38268343236508977172845998403039886676134456248561, | |
| 201 | + .19509032201612826784828486847702224092769161775195, | |
| 202 | + .09801714032956060199419556388864184586113667316749, | |
| 203 | + .04906767432741801425495497694268265831474536302574, | |
| 204 | + .02454122852291228803173452945928292506546611923944, | |
| 205 | + .01227153828571992607940826195100321214037231959176, | |
| 206 | + .00613588464915447535964023459037258091705788631738, | |
| 207 | + .00306795676296597627014536549091984251894461021344, | |
| 208 | + .00153398018628476561230369715026407907995486457522, | |
| 209 | + .00076699031874270452693856835794857664314091945205, | |
| 210 | + .00038349518757139558907246168118138126339502603495, | |
| 211 | + .00019174759731070330743990956198900093346887403385, | |
| 212 | + .00009587379909597734587051721097647635118706561284, | |
| 213 | + .00004793689960306688454900399049465887274686668768 | |
| 214 | + }; | |
| 215 | + | |
| 216 | + | |
| 217 | +#define SQRT2_2 0.70710678118654752440084436210484 | |
| 218 | +#define SQRT2 2*0.70710678118654752440084436210484 | |
| 219 | + | |
| 220 | +void mayer_fht(REAL *fz, int n) | |
| 221 | +{ | |
| 222 | +/* REAL a,b; | |
| 223 | +REAL c1,s1,s2,c2,s3,c3,s4,c4; | |
| 224 | + REAL f0,g0,f1,g1,f2,g2,f3,g3; */ | |
| 225 | + int k,k1,k2,k3,k4,kx; | |
| 226 | + REAL *fi,*fn,*gi; | |
| 227 | + TRIG_VARS; | |
| 228 | + | |
| 229 | + for (k1=1,k2=0;k1<n;k1++) | |
| 230 | + { | |
| 231 | + REAL aa; | |
| 232 | + for (k=n>>1; (!((k2^=k)&k)); k>>=1); | |
| 233 | + if (k1>k2) | |
| 234 | + { | |
| 235 | + aa=fz[k1];fz[k1]=fz[k2];fz[k2]=aa; | |
| 236 | + } | |
| 237 | + } | |
| 238 | + for ( k=0 ; (1<<k)<n ; k++ ); | |
| 239 | + k &= 1; | |
| 240 | + if (k==0) | |
| 241 | + { | |
| 242 | + for (fi=fz,fn=fz+n;fi<fn;fi+=4) | |
| 243 | + { | |
| 244 | + REAL f0,f1,f2,f3; | |
| 245 | + f1 = fi[0 ]-fi[1 ]; | |
| 246 | + f0 = fi[0 ]+fi[1 ]; | |
| 247 | + f3 = fi[2 ]-fi[3 ]; | |
| 248 | + f2 = fi[2 ]+fi[3 ]; | |
| 249 | + fi[2 ] = (f0-f2); | |
| 250 | + fi[0 ] = (f0+f2); | |
| 251 | + fi[3 ] = (f1-f3); | |
| 252 | + fi[1 ] = (f1+f3); | |
| 253 | + } | |
| 254 | + } | |
| 255 | + else | |
| 256 | + { | |
| 257 | + for (fi=fz,fn=fz+n,gi=fi+1;fi<fn;fi+=8,gi+=8) | |
| 258 | + { | |
| 259 | + REAL bs1,bc1,bs2,bc2,bs3,bc3,bs4,bc4, | |
| 260 | + bg0,bf0,bf1,bg1,bf2,bg2,bf3,bg3; | |
| 261 | + bc1 = fi[0 ] - gi[0 ]; | |
| 262 | + bs1 = fi[0 ] + gi[0 ]; | |
| 263 | + bc2 = fi[2 ] - gi[2 ]; | |
| 264 | + bs2 = fi[2 ] + gi[2 ]; | |
| 265 | + bc3 = fi[4 ] - gi[4 ]; | |
| 266 | + bs3 = fi[4 ] + gi[4 ]; | |
| 267 | + bc4 = fi[6 ] - gi[6 ]; | |
| 268 | + bs4 = fi[6 ] + gi[6 ]; | |
| 269 | + bf1 = (bs1 - bs2); | |
| 270 | + bf0 = (bs1 + bs2); | |
| 271 | + bg1 = (bc1 - bc2); | |
| 272 | + bg0 = (bc1 + bc2); | |
| 273 | + bf3 = (bs3 - bs4); | |
| 274 | + bf2 = (bs3 + bs4); | |
| 275 | + bg3 = SQRT2*bc4; | |
| 276 | + bg2 = SQRT2*bc3; | |
| 277 | + fi[4 ] = bf0 - bf2; | |
| 278 | + fi[0 ] = bf0 + bf2; | |
| 279 | + fi[6 ] = bf1 - bf3; | |
| 280 | + fi[2 ] = bf1 + bf3; | |
| 281 | + gi[4 ] = bg0 - bg2; | |
| 282 | + gi[0 ] = bg0 + bg2; | |
| 283 | + gi[6 ] = bg1 - bg3; | |
| 284 | + gi[2 ] = bg1 + bg3; | |
| 285 | + } | |
| 286 | + } | |
| 287 | + if (n<16) return; | |
| 288 | + | |
| 289 | + do | |
| 290 | + { | |
| 291 | + REAL s1,c1; | |
| 292 | + int ii; | |
| 293 | + k += 2; | |
| 294 | + k1 = 1 << k; | |
| 295 | + k2 = k1 << 1; | |
| 296 | + k4 = k2 << 1; | |
| 297 | + k3 = k2 + k1; | |
| 298 | + kx = k1 >> 1; | |
| 299 | + fi = fz; | |
| 300 | + gi = fi + kx; | |
| 301 | + fn = fz + n; | |
| 302 | + do | |
| 303 | + { | |
| 304 | + REAL g0,f0,f1,g1,f2,g2,f3,g3; | |
| 305 | + f1 = fi[0 ] - fi[k1]; | |
| 306 | + f0 = fi[0 ] + fi[k1]; | |
| 307 | + f3 = fi[k2] - fi[k3]; | |
| 308 | + f2 = fi[k2] + fi[k3]; | |
| 309 | + fi[k2] = f0 - f2; | |
| 310 | + fi[0 ] = f0 + f2; | |
| 311 | + fi[k3] = f1 - f3; | |
| 312 | + fi[k1] = f1 + f3; | |
| 313 | + g1 = gi[0 ] - gi[k1]; | |
| 314 | + g0 = gi[0 ] + gi[k1]; | |
| 315 | + g3 = SQRT2 * gi[k3]; | |
| 316 | + g2 = SQRT2 * gi[k2]; | |
| 317 | + gi[k2] = g0 - g2; | |
| 318 | + gi[0 ] = g0 + g2; | |
| 319 | + gi[k3] = g1 - g3; | |
| 320 | + gi[k1] = g1 + g3; | |
| 321 | + gi += k4; | |
| 322 | + fi += k4; | |
| 323 | + } while (fi<fn); | |
| 324 | + TRIG_INIT(k,c1,s1); | |
| 325 | + for (ii=1;ii<kx;ii++) | |
| 326 | + { | |
| 327 | + REAL c2,s2; | |
| 328 | + TRIG_NEXT(k,c1,s1); | |
| 329 | + c2 = c1*c1 - s1*s1; | |
| 330 | + s2 = 2*(c1*s1); | |
| 331 | + fn = fz + n; | |
| 332 | + fi = fz +ii; | |
| 333 | + gi = fz +k1-ii; | |
| 334 | + do | |
| 335 | + { | |
| 336 | + REAL a,b,g0,f0,f1,g1,f2,g2,f3,g3; | |
| 337 | + b = s2*fi[k1] - c2*gi[k1]; | |
| 338 | + a = c2*fi[k1] + s2*gi[k1]; | |
| 339 | + f1 = fi[0 ] - a; | |
| 340 | + f0 = fi[0 ] + a; | |
| 341 | + g1 = gi[0 ] - b; | |
| 342 | + g0 = gi[0 ] + b; | |
| 343 | + b = s2*fi[k3] - c2*gi[k3]; | |
| 344 | + a = c2*fi[k3] + s2*gi[k3]; | |
| 345 | + f3 = fi[k2] - a; | |
| 346 | + f2 = fi[k2] + a; | |
| 347 | + g3 = gi[k2] - b; | |
| 348 | + g2 = gi[k2] + b; | |
| 349 | + b = s1*f2 - c1*g3; | |
| 350 | + a = c1*f2 + s1*g3; | |
| 351 | + fi[k2] = f0 - a; | |
| 352 | + fi[0 ] = f0 + a; | |
| 353 | + gi[k3] = g1 - b; | |
| 354 | + gi[k1] = g1 + b; | |
| 355 | + b = c1*g2 - s1*f3; | |
| 356 | + a = s1*g2 + c1*f3; | |
| 357 | + gi[k2] = g0 - a; | |
| 358 | + gi[0 ] = g0 + a; | |
| 359 | + fi[k3] = f1 - b; | |
| 360 | + fi[k1] = f1 + b; | |
| 361 | + gi += k4; | |
| 362 | + fi += k4; | |
| 363 | + } while (fi<fn); | |
| 364 | + } | |
| 365 | + TRIG_RESET(k,c1,s1); | |
| 366 | + } while (k4<n); | |
| 367 | +} | |
| 368 | + | |
| 369 | +void mayer_fft(int n, REAL *real, REAL *imag) | |
| 370 | +{ | |
| 371 | + REAL a,b,c,d; | |
| 372 | + REAL q,r,s,t; | |
| 373 | + int i,j,k; | |
| 374 | + for (i=1,j=n-1,k=n/2;i<k;i++,j--) { | |
| 375 | + a = real[i]; b = real[j]; q=a+b; r=a-b; | |
| 376 | + c = imag[i]; d = imag[j]; s=c+d; t=c-d; | |
| 377 | + real[i] = (q+t)*.5; real[j] = (q-t)*.5; | |
| 378 | + imag[i] = (s-r)*.5; imag[j] = (s+r)*.5; | |
| 379 | + } | |
| 380 | + mayer_fht(real,n); | |
| 381 | + mayer_fht(imag,n); | |
| 382 | +} | |
| 383 | + | |
| 384 | +void mayer_ifft(int n, REAL *real, REAL *imag) | |
| 385 | +{ | |
| 386 | + REAL a,b,c,d; | |
| 387 | + REAL q,r,s,t; | |
| 388 | + int i,j,k; | |
| 389 | + mayer_fht(real,n); | |
| 390 | + mayer_fht(imag,n); | |
| 391 | + for (i=1,j=n-1,k=n/2;i<k;i++,j--) { | |
| 392 | + a = real[i]; b = real[j]; q=a+b; r=a-b; | |
| 393 | + c = imag[i]; d = imag[j]; s=c+d; t=c-d; | |
| 394 | + imag[i] = (s+r)*0.5; imag[j] = (s-r)*0.5; | |
| 395 | + real[i] = (q-t)*0.5; real[j] = (q+t)*0.5; | |
| 396 | + } | |
| 397 | +} | |
| 398 | + | |
| 399 | +void mayer_realfft(int n, REAL *real) | |
| 400 | +{ | |
| 401 | + REAL a,b; | |
| 402 | + int i,j,k; | |
| 403 | + mayer_fht(real,n); | |
| 404 | + for (i=1,j=n-1,k=n/2;i<k;i++,j--) { | |
| 405 | + a = real[i]; | |
| 406 | + b = real[j]; | |
| 407 | + real[j] = (a-b)*0.5; | |
| 408 | + real[i] = (a+b)*0.5; | |
| 409 | + } | |
| 410 | +} | |
| 411 | + | |
| 412 | +void mayer_realifft(int n, REAL *real) | |
| 413 | +{ | |
| 414 | + REAL a,b; | |
| 415 | + int i,j,k; | |
| 416 | + for (i=1,j=n-1,k=n/2;i<k;i++,j--) { | |
| 417 | + a = real[i]; | |
| 418 | + b = real[j]; | |
| 419 | + real[j] = (a-b); | |
| 420 | + real[i] = (a+b); | |
| 421 | + } | |
| 422 | + mayer_fht(real,n); | |
| 423 | +} | |
| @@ -0,0 +1,31 @@ | ||
| 1 | +/* | |
| 2 | + * d_fft_mayer.h | |
| 3 | + * | |
| 4 | + * Created: 5/4/2017 10:33:59 PM | |
| 5 | + * Author: Kenny | |
| 6 | + */ | |
| 7 | + | |
| 8 | + | |
| 9 | +#ifndef D_FFT_MAYER_H_ | |
| 10 | +#define D_FFT_MAYER_H_ | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | +typedef float t_sample; /* a float type at most the same size */ | |
| 17 | + | |
| 18 | +#define REAL t_sample | |
| 19 | + | |
| 20 | +void mayer_fht(REAL *fz, int n); | |
| 21 | +void mayer_fft(int n, REAL *real, REAL *imag); | |
| 22 | +void mayer_ifft(int n, REAL *real, REAL *imag); | |
| 23 | +void mayer_realfft(int n, REAL *real); | |
| 24 | +void mayer_realifft(int n, REAL *real); | |
| 25 | + | |
| 26 | + | |
| 27 | +#ifdef __cplusplus | |
| 28 | +} | |
| 29 | +#endif | |
| 30 | + | |
| 31 | +#endif /* D_FFT_MAYER_H_ */ | |
| @@ -0,0 +1,173 @@ | ||
| 1 | + | |
| 2 | +/* | |
| 3 | +** Please only distribute this with it's associated FHT routine. | |
| 4 | +** This algorithm is apparently patented(!) and the code copyrighted. | |
| 5 | +** See the comment with the fht routine for more info. | |
| 6 | +** -Thanks, | |
| 7 | +** Ron Mayer | |
| 8 | +*/ | |
| 9 | + | |
| 10 | +#ifdef __cplusplus | |
| 11 | +extern "C" { | |
| 12 | +#endif | |
| 13 | + | |
| 14 | +#ifdef REAL | |
| 15 | +#else | |
| 16 | +#define REAL double | |
| 17 | +#endif | |
| 18 | + | |
| 19 | +#ifdef GOOD_TRIG | |
| 20 | +#else | |
| 21 | +#define FAST_TRIG | |
| 22 | +#endif | |
| 23 | + | |
| 24 | +#if defined(GOOD_TRIG) | |
| 25 | +#define FHT_SWAP(a,b,t) {(t)=(a);(a)=(b);(b)=(t);} | |
| 26 | +#define TRIG_VARS \ | |
| 27 | + int t_lam=0; | |
| 28 | +#define TRIG_INIT(k,c,s) \ | |
| 29 | + { \ | |
| 30 | + int i; \ | |
| 31 | + for (i=2 ; i<=k ; i++) \ | |
| 32 | + {coswrk[i]=costab[i];sinwrk[i]=sintab[i];} \ | |
| 33 | + t_lam = 0; \ | |
| 34 | + c = 1; \ | |
| 35 | + s = 0; \ | |
| 36 | + } | |
| 37 | +#define TRIG_NEXT(k,c,s) \ | |
| 38 | + { \ | |
| 39 | + int i,j; \ | |
| 40 | + (t_lam)++; \ | |
| 41 | + for (i=0 ; !((1<<i)&t_lam) ; i++); \ | |
| 42 | + i = k-i; \ | |
| 43 | + s = sinwrk[i]; \ | |
| 44 | + c = coswrk[i]; \ | |
| 45 | + if (i>1) \ | |
| 46 | + { \ | |
| 47 | + for (j=k-i+2 ; (1<<j)&t_lam ; j++); \ | |
| 48 | + j = k - j; \ | |
| 49 | + sinwrk[i] = halsec[i] * (sinwrk[i-1] + sinwrk[j]); \ | |
| 50 | + coswrk[i] = halsec[i] * (coswrk[i-1] + coswrk[j]); \ | |
| 51 | + } \ | |
| 52 | + } | |
| 53 | +#define TRIG_RESET(k,c,s) | |
| 54 | +#endif | |
| 55 | + | |
| 56 | +#if defined(FAST_TRIG) | |
| 57 | +#define TRIG_VARS \ | |
| 58 | + REAL t_c,t_s; | |
| 59 | +#define TRIG_INIT(k,c,s) \ | |
| 60 | + { \ | |
| 61 | + t_c = costab[k]; \ | |
| 62 | + t_s = sintab[k]; \ | |
| 63 | + c = 1; \ | |
| 64 | + s = 0; \ | |
| 65 | + } | |
| 66 | +#define TRIG_NEXT(k,c,s) \ | |
| 67 | + { \ | |
| 68 | + REAL t = c; \ | |
| 69 | + c = t*t_c - s*t_s; \ | |
| 70 | + s = t*t_s + s*t_c; \ | |
| 71 | + } | |
| 72 | +#define TRIG_RESET(k,c,s) | |
| 73 | +#endif | |
| 74 | + | |
| 75 | +static REAL halsec[20]= | |
| 76 | + { | |
| 77 | + 0, | |
| 78 | + 0, | |
| 79 | + .54119610014619698439972320536638942006107206337801, | |
| 80 | + .50979557910415916894193980398784391368261849190893, | |
| 81 | + .50241928618815570551167011928012092247859337193963, | |
| 82 | + .50060299823519630134550410676638239611758632599591, | |
| 83 | + .50015063602065098821477101271097658495974913010340, | |
| 84 | + .50003765191554772296778139077905492847503165398345, | |
| 85 | + .50000941253588775676512870469186533538523133757983, | |
| 86 | + .50000235310628608051401267171204408939326297376426, | |
| 87 | + .50000058827484117879868526730916804925780637276181, | |
| 88 | + .50000014706860214875463798283871198206179118093251, | |
| 89 | + .50000003676714377807315864400643020315103490883972, | |
| 90 | + .50000000919178552207366560348853455333939112569380, | |
| 91 | + .50000000229794635411562887767906868558991922348920, | |
| 92 | + .50000000057448658687873302235147272458812263401372 | |
| 93 | + }; | |
| 94 | +static REAL costab[20]= | |
| 95 | + { | |
| 96 | + .00000000000000000000000000000000000000000000000000, | |
| 97 | + .70710678118654752440084436210484903928483593768847, | |
| 98 | + .92387953251128675612818318939678828682241662586364, | |
| 99 | + .98078528040323044912618223613423903697393373089333, | |
| 100 | + .99518472667219688624483695310947992157547486872985, | |
| 101 | + .99879545620517239271477160475910069444320361470461, | |
| 102 | + .99969881869620422011576564966617219685006108125772, | |
| 103 | + .99992470183914454092164649119638322435060646880221, | |
| 104 | + .99998117528260114265699043772856771617391725094433, | |
| 105 | + .99999529380957617151158012570011989955298763362218, | |
| 106 | + .99999882345170190992902571017152601904826792288976, | |
| 107 | + .99999970586288221916022821773876567711626389934930, | |
| 108 | + .99999992646571785114473148070738785694820115568892, | |
| 109 | + .99999998161642929380834691540290971450507605124278, | |
| 110 | + .99999999540410731289097193313960614895889430318945, | |
| 111 | + .99999999885102682756267330779455410840053741619428 | |
| 112 | + }; | |
| 113 | +static REAL sintab[20]= | |
| 114 | + { | |
| 115 | + 1.0000000000000000000000000000000000000000000000000, | |
| 116 | + .70710678118654752440084436210484903928483593768846, | |
| 117 | + .38268343236508977172845998403039886676134456248561, | |
| 118 | + .19509032201612826784828486847702224092769161775195, | |
| 119 | + .09801714032956060199419556388864184586113667316749, | |
| 120 | + .04906767432741801425495497694268265831474536302574, | |
| 121 | + .02454122852291228803173452945928292506546611923944, | |
| 122 | + .01227153828571992607940826195100321214037231959176, | |
| 123 | + .00613588464915447535964023459037258091705788631738, | |
| 124 | + .00306795676296597627014536549091984251894461021344, | |
| 125 | + .00153398018628476561230369715026407907995486457522, | |
| 126 | + .00076699031874270452693856835794857664314091945205, | |
| 127 | + .00038349518757139558907246168118138126339502603495, | |
| 128 | + .00019174759731070330743990956198900093346887403385, | |
| 129 | + .00009587379909597734587051721097647635118706561284, | |
| 130 | + .00004793689960306688454900399049465887274686668768 | |
| 131 | + }; | |
| 132 | +static REAL coswrk[20]= | |
| 133 | + { | |
| 134 | + .00000000000000000000000000000000000000000000000000, | |
| 135 | + .70710678118654752440084436210484903928483593768847, | |
| 136 | + .92387953251128675612818318939678828682241662586364, | |
| 137 | + .98078528040323044912618223613423903697393373089333, | |
| 138 | + .99518472667219688624483695310947992157547486872985, | |
| 139 | + .99879545620517239271477160475910069444320361470461, | |
| 140 | + .99969881869620422011576564966617219685006108125772, | |
| 141 | + .99992470183914454092164649119638322435060646880221, | |
| 142 | + .99998117528260114265699043772856771617391725094433, | |
| 143 | + .99999529380957617151158012570011989955298763362218, | |
| 144 | + .99999882345170190992902571017152601904826792288976, | |
| 145 | + .99999970586288221916022821773876567711626389934930, | |
| 146 | + .99999992646571785114473148070738785694820115568892, | |
| 147 | + .99999998161642929380834691540290971450507605124278, | |
| 148 | + .99999999540410731289097193313960614895889430318945, | |
| 149 | + .99999999885102682756267330779455410840053741619428 | |
| 150 | + }; | |
| 151 | +static REAL sinwrk[20]= | |
| 152 | + { | |
| 153 | + 1.0000000000000000000000000000000000000000000000000, | |
| 154 | + .70710678118654752440084436210484903928483593768846, | |
| 155 | + .38268343236508977172845998403039886676134456248561, | |
| 156 | + .19509032201612826784828486847702224092769161775195, | |
| 157 | + .09801714032956060199419556388864184586113667316749, | |
| 158 | + .04906767432741801425495497694268265831474536302574, | |
| 159 | + .02454122852291228803173452945928292506546611923944, | |
| 160 | + .01227153828571992607940826195100321214037231959176, | |
| 161 | + .00613588464915447535964023459037258091705788631738, | |
| 162 | + .00306795676296597627014536549091984251894461021344, | |
| 163 | + .00153398018628476561230369715026407907995486457522, | |
| 164 | + .00076699031874270452693856835794857664314091945205, | |
| 165 | + .00038349518757139558907246168118138126339502603495, | |
| 166 | + .00019174759731070330743990956198900093346887403385, | |
| 167 | + .00009587379909597734587051721097647635118706561284, | |
| 168 | + .00004793689960306688454900399049465887274686668768 | |
| 169 | + }; | |
| 170 | + | |
| 171 | +#ifdef __cplusplus | |
| 172 | +} | |
| 173 | +#endif | |
| @@ -0,0 +1,296 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-analysis.h | |
| 4 | + Created: 25 Oct 2019 10:30:52am | |
| 5 | + Author: Matthew Wang | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_ANALYSIS_H_INCLUDED | |
| 10 | +#define LEAF_ANALYSIS_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | + //============================================================================== | |
| 17 | + | |
| 18 | +#include "leaf-global.h" | |
| 19 | +#include "leaf-mempool.h" | |
| 20 | +#include "leaf-math.h" | |
| 21 | +#include "leaf-filters.h" | |
| 22 | +#include "leaf-envelopes.h" | |
| 23 | + | |
| 24 | + /*! | |
| 25 | + * @internal | |
| 26 | + * Header. | |
| 27 | + * @include basic-oscillators.h | |
| 28 | + * @example basic-oscillators.c | |
| 29 | + * An example. | |
| 30 | + */ | |
| 31 | + | |
| 32 | + //============================================================================== | |
| 33 | + | |
| 34 | + /* Envelope Follower */ | |
| 35 | + typedef struct _tEnvelopeFollower | |
| 36 | + { | |
| 37 | + float y; | |
| 38 | + float a_thresh; | |
| 39 | + float d_coeff; | |
| 40 | + | |
| 41 | + } _tEnvelopeFollower; | |
| 42 | + | |
| 43 | + typedef _tEnvelopeFollower* tEnvelopeFollower; | |
| 44 | + | |
| 45 | + void tEnvelopeFollower_init (tEnvelopeFollower* const, float attackThreshold, float decayCoeff); | |
| 46 | + void tEnvelopeFollower_free (tEnvelopeFollower* const); | |
| 47 | + void tEnvelopeFollower_initToPool (tEnvelopeFollower* const, float attackThreshold, float decayCoeff, tMempool* const); | |
| 48 | + void tEnvelopeFollower_freeFromPool (tEnvelopeFollower* const, tMempool* const); | |
| 49 | + | |
| 50 | + float tEnvelopeFollower_tick (tEnvelopeFollower* const, float x); | |
| 51 | + int tEnvelopeFollower_decayCoeff (tEnvelopeFollower* const, float decayCoeff); | |
| 52 | + int tEnvelopeFollower_attackThresh (tEnvelopeFollower* const, float attackThresh); | |
| 53 | + | |
| 54 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 55 | + | |
| 56 | + /* Zero Crossing Detector */ | |
| 57 | + typedef struct _tZeroCrossing { | |
| 58 | + int count; | |
| 59 | + int maxWindowSize; | |
| 60 | + int currentWindowSize; | |
| 61 | + float invCurrentWindowSize; | |
| 62 | + float* inBuffer; | |
| 63 | + uint16_t* countBuffer; | |
| 64 | + int prevPosition; | |
| 65 | + int position; | |
| 66 | + } _tZeroCrossing; | |
| 67 | + | |
| 68 | + typedef _tZeroCrossing* tZeroCrossing; | |
| 69 | + | |
| 70 | + void tZeroCrossing_init (tZeroCrossing* const, int maxWindowSize); | |
| 71 | + void tZeroCrossing_free (tZeroCrossing* const); | |
| 72 | + void tZeroCrossing_initToPool (tZeroCrossing* const, int maxWindowSize, tMempool* const); | |
| 73 | + void tZeroCrossing_freeFromPool (tZeroCrossing* const, tMempool* const); | |
| 74 | + | |
| 75 | + float tZeroCrossing_tick (tZeroCrossing* const, float input); | |
| 76 | + void tZeroCrossing_setWindow (tZeroCrossing* const, float windowSize); | |
| 77 | + | |
| 78 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 79 | + | |
| 80 | + /* PowerEnvelopeFollower */ | |
| 81 | + typedef struct _tPowerFollower { | |
| 82 | + float factor, oneminusfactor; | |
| 83 | + float curr; | |
| 84 | + | |
| 85 | + } _tPowerFollower; | |
| 86 | + | |
| 87 | + typedef _tPowerFollower* tPowerFollower; | |
| 88 | + | |
| 89 | + void tPowerFollower_init (tPowerFollower* const, float factor); | |
| 90 | + void tPowerFollower_free (tPowerFollower* const); | |
| 91 | + void tPowerFollower_initToPool (tPowerFollower* const, float factor, tMempool* const); | |
| 92 | + void tPowerFollower_freeFromPool (tPowerFollower* const, tMempool* const); | |
| 93 | + | |
| 94 | + float tPowerFollower_tick (tPowerFollower* const, float input); | |
| 95 | + float tPowerFollower_sample (tPowerFollower* const); | |
| 96 | + int tPowerFollower_setFactor (tPowerFollower* const, float factor); | |
| 97 | + | |
| 98 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 99 | + | |
| 100 | + // ENV~ from PD, modified for LEAF | |
| 101 | +#define MAXOVERLAP 32 | |
| 102 | +#define INITVSTAKEN 64 | |
| 103 | +#define ENV_WINDOW_SIZE 1024 | |
| 104 | +#define ENV_HOP_SIZE 256 | |
| 105 | + | |
| 106 | + typedef struct _tEnvPD | |
| 107 | + { | |
| 108 | + float buf[ENV_WINDOW_SIZE + INITVSTAKEN]; | |
| 109 | + uint16_t x_phase; /* number of points since last output */ | |
| 110 | + uint16_t x_period; /* requested period of output */ | |
| 111 | + uint16_t x_realperiod; /* period rounded up to vecsize multiple */ | |
| 112 | + uint16_t x_npoints; /* analysis window size in samples */ | |
| 113 | + float x_result; /* result to output */ | |
| 114 | + float x_sumbuf[MAXOVERLAP]; /* summing buffer */ | |
| 115 | + float x_f; | |
| 116 | + uint16_t windowSize, hopSize, blockSize; | |
| 117 | + uint16_t x_allocforvs; /* extra buffer for DSP vector size */ | |
| 118 | + } _tEnvPD; | |
| 119 | + | |
| 120 | + typedef _tEnvPD* tEnvPD; | |
| 121 | + | |
| 122 | + void tEnvPD_init (tEnvPD* const, int windowSize, int hopSize, int blockSize); | |
| 123 | + void tEnvPD_free (tEnvPD* const); | |
| 124 | + void tEnvPD_initToPool (tEnvPD* const, int windowSize, int hopSize, int blockSize, tMempool* const); | |
| 125 | + void tEnvPD_freeFromPool (tEnvPD* const, tMempool* const); | |
| 126 | + | |
| 127 | + float tEnvPD_tick (tEnvPD* const); | |
| 128 | + void tEnvPD_processBlock (tEnvPD* const, float* in); | |
| 129 | + | |
| 130 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 131 | + | |
| 132 | + /* tAttackDetection */ | |
| 133 | +#define DEFBLOCKSIZE 1024 | |
| 134 | +#define DEFTHRESHOLD 6 | |
| 135 | +#define DEFATTACK 10 | |
| 136 | +#define DEFRELEASE 10 | |
| 137 | + | |
| 138 | + typedef struct _tAttackDetection | |
| 139 | + { | |
| 140 | + float env; | |
| 141 | + | |
| 142 | + //Attack & Release times in msec | |
| 143 | + int atk; | |
| 144 | + int rel; | |
| 145 | + | |
| 146 | + //Attack & Release coefficients based on times | |
| 147 | + float atk_coeff; | |
| 148 | + float rel_coeff; | |
| 149 | + | |
| 150 | + int blocksize; | |
| 151 | + int samplerate; | |
| 152 | + | |
| 153 | + //RMS amplitude of previous block - used to decide if attack is present | |
| 154 | + float prevAmp; | |
| 155 | + | |
| 156 | + float threshold; | |
| 157 | + } _tAttackDetection; | |
| 158 | + | |
| 159 | + typedef _tAttackDetection* tAttackDetection; | |
| 160 | + | |
| 161 | + void tAttackDetection_init (tAttackDetection* const, int blocksize, int atk, int rel); | |
| 162 | + void tAttackDetection_free (tAttackDetection* const); | |
| 163 | + void tAttackDetection_initToPool (tAttackDetection* const, int blocksize, int atk, int rel, tMempool* const); | |
| 164 | + void tAttackDetection_freeFromPool (tAttackDetection* const, tMempool* const); | |
| 165 | + | |
| 166 | + // set expected input blocksize | |
| 167 | + void tAttackDetection_setBlocksize (tAttackDetection* const, int size); | |
| 168 | + | |
| 169 | + // change atkDetector sample rate | |
| 170 | + void tAttackDetection_setSamplerate (tAttackDetection* const, int inRate); | |
| 171 | + | |
| 172 | + // set attack time and coeff | |
| 173 | + void tAttackDetection_setAttack (tAttackDetection* const, int inAtk); | |
| 174 | + | |
| 175 | + // set release time and coeff | |
| 176 | + void tAttackDetection_setRelease (tAttackDetection* const, int inRel); | |
| 177 | + | |
| 178 | + // set level above which values are identified as attacks | |
| 179 | + void tAttackDetection_setThreshold (tAttackDetection* const, float thres); | |
| 180 | + | |
| 181 | + // find largest transient in input block, return index of attack | |
| 182 | + int tAttackDetection_detect (tAttackDetection* const, float *in); | |
| 183 | + | |
| 184 | + //============================================================================== | |
| 185 | + | |
| 186 | + // tSNAC: period detector | |
| 187 | + // from Katja Vetters http://www.katjaas.nl/helmholtz/helmholtz.html | |
| 188 | +#define SNAC_FRAME_SIZE 1024 // default analysis framesize // should be the same as (or smaller than?) PS_FRAME_SIZE | |
| 189 | +#define DEFOVERLAP 1 // default overlap | |
| 190 | +#define DEFBIAS 0.2f // default bias | |
| 191 | +#define DEFMINRMS 0.003f // default minimum RMS | |
| 192 | +#define SEEK 0.85f // seek-length as ratio of framesize | |
| 193 | + | |
| 194 | + typedef struct _tSNAC | |
| 195 | + { | |
| 196 | + float* inputbuf; | |
| 197 | + float* processbuf; | |
| 198 | + float* spectrumbuf; | |
| 199 | + float* biasbuf; | |
| 200 | + uint16_t timeindex; | |
| 201 | + uint16_t framesize; | |
| 202 | + uint16_t overlap; | |
| 203 | + uint16_t periodindex; | |
| 204 | + | |
| 205 | + float periodlength; | |
| 206 | + float fidelity; | |
| 207 | + float biasfactor; | |
| 208 | + float minrms; | |
| 209 | + | |
| 210 | + } _tSNAC; | |
| 211 | + | |
| 212 | + typedef _tSNAC* tSNAC; | |
| 213 | + | |
| 214 | + void tSNAC_init (tSNAC* const, int overlaparg); | |
| 215 | + void tSNAC_free (tSNAC* const); | |
| 216 | + void tSNAC_initToPool (tSNAC* const, int overlaparg, tMempool* const); | |
| 217 | + void tSNAC_freeFromPool (tSNAC* const, tMempool* const); | |
| 218 | + | |
| 219 | + void tSNAC_ioSamples (tSNAC *s, float *in, float *out, int size); | |
| 220 | + void tSNAC_setOverlap (tSNAC *s, int lap); | |
| 221 | + void tSNAC_setBias (tSNAC *s, float bias); | |
| 222 | + void tSNAC_setMinRMS (tSNAC *s, float rms); | |
| 223 | + | |
| 224 | + /*To get freq, perform SAMPLE_RATE/snac_getperiod() */ | |
| 225 | + float tSNAC_getPeriod (tSNAC *s); | |
| 226 | + float tSNAC_getFidelity (tSNAC *s); | |
| 227 | + | |
| 228 | +#define DEFPITCHRATIO 2.0f | |
| 229 | +#define DEFTIMECONSTANT 100.0f | |
| 230 | +#define DEFHOPSIZE 64 | |
| 231 | +#define DEFWINDOWSIZE 64 | |
| 232 | +#define FBA 20 | |
| 233 | +#define HPFREQ 40.0f | |
| 234 | + | |
| 235 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 236 | + // Period detection | |
| 237 | + typedef struct _tPeriodDetection | |
| 238 | + { | |
| 239 | + tEnvPD env; | |
| 240 | + tSNAC snac; | |
| 241 | + float* inBuffer; | |
| 242 | + float* outBuffer; | |
| 243 | + int frameSize; | |
| 244 | + int bufSize; | |
| 245 | + int framesPerBuffer; | |
| 246 | + int curBlock; | |
| 247 | + int lastBlock; | |
| 248 | + int i; | |
| 249 | + int indexstore; | |
| 250 | + int iLast; | |
| 251 | + int index; | |
| 252 | + float period; | |
| 253 | + | |
| 254 | + uint16_t hopSize; | |
| 255 | + uint16_t windowSize; | |
| 256 | + uint8_t fba; | |
| 257 | + | |
| 258 | + float timeConstant; | |
| 259 | + float radius; | |
| 260 | + float max; | |
| 261 | + float lastmax; | |
| 262 | + float deltamax; | |
| 263 | + | |
| 264 | + float fidelityThreshold; | |
| 265 | + | |
| 266 | + float history; | |
| 267 | + float alpha; | |
| 268 | + float tolerance; | |
| 269 | + | |
| 270 | + } _tPeriodDetection; | |
| 271 | + | |
| 272 | + typedef _tPeriodDetection* tPeriodDetection; | |
| 273 | + | |
| 274 | + void tPeriodDetection_init (tPeriodDetection* const, float* in, float* out, int bufSize, int frameSize); | |
| 275 | + void tPeriodDetection_free (tPeriodDetection* const); | |
| 276 | + void tPeriodDetection_initToPool (tPeriodDetection* const, float* in, float* out, int bufSize, int frameSize, tMempool* const); | |
| 277 | + void tPeriodDetection_freeFromPool (tPeriodDetection* const, tMempool* const); | |
| 278 | + | |
| 279 | + float tPeriodDetection_tick (tPeriodDetection* const, float sample); | |
| 280 | + float tPeriodDetection_getPeriod (tPeriodDetection* const); | |
| 281 | + void tPeriodDetection_setHopSize (tPeriodDetection* const, int hs); | |
| 282 | + void tPeriodDetection_setWindowSize (tPeriodDetection* const, int ws); | |
| 283 | + void tPeriodDetection_setFidelityThreshold(tPeriodDetection* const, float threshold); | |
| 284 | + void tPeriodDetection_setAlpha (tPeriodDetection* const, float alpha); | |
| 285 | + void tPeriodDetection_setTolerance (tPeriodDetection* const, float tolerance); | |
| 286 | + | |
| 287 | + //============================================================================== | |
| 288 | + | |
| 289 | +#ifdef __cplusplus | |
| 290 | +} | |
| 291 | +#endif | |
| 292 | + | |
| 293 | +#endif // LEAF_ANALYSIS_H_INCLUDED | |
| 294 | + | |
| 295 | +//============================================================================== | |
| 296 | + | |
| @@ -0,0 +1,229 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-delay.h | |
| 4 | + Created: 20 Jan 2017 12:01:24pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_DELAY_H_INCLUDED | |
| 10 | +#define LEAF_DELAY_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | + //============================================================================== | |
| 17 | + | |
| 18 | +#include "leaf-math.h" | |
| 19 | +#include "leaf-mempool.h" | |
| 20 | + | |
| 21 | + /*! | |
| 22 | + * @internal | |
| 23 | + * Header. | |
| 24 | + * @include basic-oscillators.h | |
| 25 | + * @example basic-oscillators.c | |
| 26 | + * An example. | |
| 27 | + */ | |
| 28 | + | |
| 29 | + //============================================================================== | |
| 30 | + | |
| 31 | + /* Non-interpolating delay, reimplemented from STK (Cook and Scavone). */ | |
| 32 | + typedef struct _tDelay | |
| 33 | + { | |
| 34 | + float gain; | |
| 35 | + float* buff; | |
| 36 | + | |
| 37 | + float lastOut, lastIn; | |
| 38 | + | |
| 39 | + uint32_t inPoint, outPoint; | |
| 40 | + | |
| 41 | + uint32_t delay, maxDelay; | |
| 42 | + | |
| 43 | + } _tDelay; | |
| 44 | + | |
| 45 | + typedef _tDelay* tDelay; | |
| 46 | + | |
| 47 | + void tDelay_init (tDelay* const, uint32_t delay, uint32_t maxDelay); | |
| 48 | + void tDelay_free (tDelay* const); | |
| 49 | + void tDelay_initToPool (tDelay* const, uint32_t delay, uint32_t maxDelay, tMempool* const); | |
| 50 | + void tDelay_freeFromPool (tDelay* const, tMempool* const); | |
| 51 | + | |
| 52 | + void tDelay_clear (tDelay* const); | |
| 53 | + int tDelay_setDelay (tDelay* const, uint32_t delay); | |
| 54 | + uint32_t tDelay_getDelay (tDelay* const); | |
| 55 | + void tDelay_tapIn (tDelay* const, float in, uint32_t tapDelay); | |
| 56 | + float tDelay_tapOut (tDelay* const, uint32_t tapDelay); | |
| 57 | + float tDelay_addTo (tDelay* const, float value, uint32_t tapDelay); | |
| 58 | + float tDelay_tick (tDelay* const, float sample); | |
| 59 | + float tDelay_getLastOut (tDelay* const); | |
| 60 | + float tDelay_getLastIn (tDelay* const); | |
| 61 | + | |
| 62 | + //============================================================================== | |
| 63 | + | |
| 64 | + /* Linearly-interpolating delay, reimplemented from STK (Cook and Scavone). */ | |
| 65 | + typedef struct _tLinearDelay | |
| 66 | + { | |
| 67 | + float gain; | |
| 68 | + float* buff; | |
| 69 | + | |
| 70 | + float lastOut, lastIn; | |
| 71 | + | |
| 72 | + uint32_t inPoint, outPoint; | |
| 73 | + | |
| 74 | + uint32_t maxDelay; | |
| 75 | + | |
| 76 | + float delay; | |
| 77 | + | |
| 78 | + float alpha, omAlpha; | |
| 79 | + | |
| 80 | + } _tLinearDelay; | |
| 81 | + | |
| 82 | + typedef _tLinearDelay* tLinearDelay; | |
| 83 | + | |
| 84 | + void tLinearDelay_init (tLinearDelay* const, float delay, uint32_t maxDelay); | |
| 85 | + void tLinearDelay_free (tLinearDelay* const); | |
| 86 | + void tLinearDelay_initToPool (tLinearDelay* const, float delay, uint32_t maxDelay, tMempool* const); | |
| 87 | + void tLinearDelay_freeFromPool(tLinearDelay* const, tMempool* const); | |
| 88 | + void tLinearDelay_clear (tLinearDelay* const dl); | |
| 89 | + int tLinearDelay_setDelay (tLinearDelay* const, float delay); | |
| 90 | + float tLinearDelay_getDelay (tLinearDelay* const); | |
| 91 | + void tLinearDelay_tapIn (tLinearDelay* const, float in, uint32_t tapDelay); | |
| 92 | + float tLinearDelay_tapOut (tLinearDelay* const, uint32_t tapDelay); | |
| 93 | + float tLinearDelay_addTo (tLinearDelay* const, float value, uint32_t tapDelay); | |
| 94 | + float tLinearDelay_tick (tLinearDelay* const, float sample); | |
| 95 | + void tLinearDelay_tickIn (tLinearDelay* const, float input); | |
| 96 | + float tLinearDelay_tickOut (tLinearDelay* const); | |
| 97 | + float tLinearDelay_getLastOut (tLinearDelay* const); | |
| 98 | + float tLinearDelay_getLastIn (tLinearDelay* const); | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + //============================================================================== | |
| 103 | + | |
| 104 | + /* Hermite-interpolating delay, created by adapting STK linear delay with Hermite interpolation */ | |
| 105 | + typedef struct _tHermiteDelay | |
| 106 | + { | |
| 107 | + float gain; | |
| 108 | + float* buff; | |
| 109 | + | |
| 110 | + float lastOut, lastIn; | |
| 111 | + | |
| 112 | + uint32_t inPoint, outPoint; | |
| 113 | + | |
| 114 | + uint32_t maxDelay; | |
| 115 | + | |
| 116 | + float delay; | |
| 117 | + | |
| 118 | + float alpha, omAlpha; | |
| 119 | + | |
| 120 | + } _tHermiteDelay; | |
| 121 | + | |
| 122 | + typedef _tHermiteDelay* tHermiteDelay; | |
| 123 | + | |
| 124 | + void tHermiteDelay_init (tHermiteDelay* const dl, float delay, uint32_t maxDelay); | |
| 125 | + void tHermiteDelay_free (tHermiteDelay* const dl); | |
| 126 | + void tHermiteDelay_initToPool (tHermiteDelay* const dl, float delay, uint32_t maxDelay, tMempool* const mp); | |
| 127 | + void tHermiteDelay_freeFromPool (tHermiteDelay* const dl, tMempool* const mp); | |
| 128 | + void tHermiteDelay_clear (tHermiteDelay* const dl); | |
| 129 | + float tHermiteDelay_tick (tHermiteDelay* const dl, float input); | |
| 130 | + void tHermiteDelay_tickIn (tHermiteDelay* const dl, float input); | |
| 131 | + float tHermiteDelay_tickOut (tHermiteDelay* const dl); | |
| 132 | + int tHermiteDelay_setDelay (tHermiteDelay* const dl, float delay); | |
| 133 | + float tHermiteDelay_tapOut (tHermiteDelay* const dl, uint32_t tapDelay); | |
| 134 | + void tHermiteDelay_tapIn (tHermiteDelay* const dl, float value, uint32_t tapDelay); | |
| 135 | + float tHermiteDelay_addTo (tHermiteDelay* const dl, float value, uint32_t tapDelay); | |
| 136 | + float tHermiteDelay_getDelay (tHermiteDelay* const dl); | |
| 137 | + float tHermiteDelay_getLastOut (tHermiteDelay* const dl); | |
| 138 | + float tHermiteDelay_getLastIn (tHermiteDelay* const dl); | |
| 139 | + void tHermiteDelay_setGain (tHermiteDelay* const dl, float gain); | |
| 140 | + float tHermiteDelay_getGain (tHermiteDelay* const dl); | |
| 141 | + | |
| 142 | + | |
| 143 | + //============================================================================== | |
| 144 | + | |
| 145 | + /* Allpass-interpolating delay, reimplemented from STK (Cook and Scavone). */ | |
| 146 | + typedef struct _tAllpassDelay | |
| 147 | + { | |
| 148 | + float gain; | |
| 149 | + float* buff; | |
| 150 | + | |
| 151 | + float lastOut, lastIn; | |
| 152 | + | |
| 153 | + uint32_t inPoint, outPoint; | |
| 154 | + | |
| 155 | + uint32_t maxDelay; | |
| 156 | + | |
| 157 | + float delay; | |
| 158 | + | |
| 159 | + float alpha, omAlpha, coeff; | |
| 160 | + | |
| 161 | + float apInput; | |
| 162 | + | |
| 163 | + } _tAllpassDelay; | |
| 164 | + | |
| 165 | + typedef _tAllpassDelay* tAllpassDelay; | |
| 166 | + | |
| 167 | + void tAllpassDelay_init (tAllpassDelay* const, float delay, uint32_t maxDelay); | |
| 168 | + void tAllpassDelay_free (tAllpassDelay* const); | |
| 169 | + void tAllpassDelay_initToPool (tAllpassDelay* const, float delay, uint32_t maxDelay, tMempool* const); | |
| 170 | + void tAllpassDelay_freeFromPool(tAllpassDelay* const, tMempool* const); | |
| 171 | + | |
| 172 | + void tAllpassDelay_clear (tAllpassDelay* const); | |
| 173 | + int tAllpassDelay_setDelay (tAllpassDelay* const, float delay); | |
| 174 | + float tAllpassDelay_getDelay (tAllpassDelay* const); | |
| 175 | + void tAllpassDelay_tapIn (tAllpassDelay* const, float in, uint32_t tapDelay); | |
| 176 | + float tAllpassDelay_tapOut (tAllpassDelay* const, uint32_t tapDelay); | |
| 177 | + float tAllpassDelay_addTo (tAllpassDelay* const, float value, uint32_t tapDelay); | |
| 178 | + float tAllpassDelay_tick (tAllpassDelay* const, float sample); | |
| 179 | + float tAllpassDelay_getLastOut (tAllpassDelay* const); | |
| 180 | + float tAllpassDelay_getLastIn (tAllpassDelay* const); | |
| 181 | + | |
| 182 | + //============================================================================== | |
| 183 | + | |
| 184 | + /* Linear interpolating delay with fixed read and write pointers, variable rate. */ | |
| 185 | + typedef struct _tTapeDelay | |
| 186 | + { | |
| 187 | + float gain; | |
| 188 | + float* buff; | |
| 189 | + | |
| 190 | + float lastOut, lastIn; | |
| 191 | + | |
| 192 | + uint32_t inPoint; | |
| 193 | + | |
| 194 | + uint32_t maxDelay; | |
| 195 | + | |
| 196 | + float delay, inc, idx; | |
| 197 | + | |
| 198 | + float apInput; | |
| 199 | + | |
| 200 | + } _tTapeDelay; | |
| 201 | + | |
| 202 | + typedef _tTapeDelay* tTapeDelay; | |
| 203 | + | |
| 204 | + void tTapeDelay_init (tTapeDelay* const, float delay, uint32_t maxDelay); | |
| 205 | + void tTapeDelay_free (tTapeDelay* const); | |
| 206 | + void tTapeDelay_initToPool (tTapeDelay* const, float delay, uint32_t maxDelay, tMempool* const); | |
| 207 | + void tTapeDelay_freeFromPool(tTapeDelay* const, tMempool* const); | |
| 208 | + | |
| 209 | + void tTapeDelay_clear (tTapeDelay* const); | |
| 210 | + void tTapeDelay_setDelay (tTapeDelay* const, float delay); | |
| 211 | + float tTapeDelay_getDelay (tTapeDelay* const); | |
| 212 | + void tTapeDelay_tapIn (tTapeDelay* const, float in, uint32_t tapDelay); | |
| 213 | + float tTapeDelay_tapOut (tTapeDelay* const d, float tapDelay); | |
| 214 | + float tTapeDelay_addTo (tTapeDelay* const, float value, uint32_t tapDelay); | |
| 215 | + float tTapeDelay_tick (tTapeDelay* const, float sample); | |
| 216 | + void tTapeDelay_incrementInPoint(tTapeDelay* const dl); | |
| 217 | + float tTapeDelay_getLastOut (tTapeDelay* const); | |
| 218 | + float tTapeDelay_getLastIn (tTapeDelay* const); | |
| 219 | + | |
| 220 | + //============================================================================== | |
| 221 | + | |
| 222 | +#ifdef __cplusplus | |
| 223 | +} | |
| 224 | +#endif | |
| 225 | + | |
| 226 | +#endif // LEAF_DELAY_H_INCLUDED | |
| 227 | + | |
| 228 | +//============================================================================== | |
| 229 | + | |
| @@ -0,0 +1,164 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-distortion.h | |
| 4 | + Created: 25 Oct 2019 10:23:28am | |
| 5 | + Author: Matthew Wang | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_DISTORTION_H_INCLUDED | |
| 10 | +#define LEAF_DISTORTION_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | + //============================================================================== | |
| 17 | + | |
| 18 | +#include "leaf-global.h" | |
| 19 | +#include "leaf-mempool.h" | |
| 20 | +#include "leaf-math.h" | |
| 21 | + | |
| 22 | + /*! | |
| 23 | + * @internal | |
| 24 | + * Header. | |
| 25 | + * @include basic-oscillators.h | |
| 26 | + * @example basic-oscillators.c | |
| 27 | + * An example. | |
| 28 | + */ | |
| 29 | + | |
| 30 | + //============================================================================== | |
| 31 | + | |
| 32 | + typedef struct _tSampleReducer | |
| 33 | + { | |
| 34 | + float invRatio; | |
| 35 | + float hold; | |
| 36 | + uint32_t count; | |
| 37 | + } _tSampleReducer; | |
| 38 | + | |
| 39 | + typedef _tSampleReducer* tSampleReducer; | |
| 40 | + | |
| 41 | + void tSampleReducer_init (tSampleReducer* const); | |
| 42 | + void tSampleReducer_free (tSampleReducer* const); | |
| 43 | + void tSampleReducer_initToPool (tSampleReducer* const, tMempool* const); | |
| 44 | + void tSampleReducer_freeFromPool (tSampleReducer* const, tMempool* const); | |
| 45 | + | |
| 46 | + float tSampleReducer_tick (tSampleReducer* const, float input); | |
| 47 | + | |
| 48 | + // sampling ratio | |
| 49 | + void tSampleReducer_setRatio (tSampleReducer* const, float ratio); | |
| 50 | + | |
| 51 | + //============================================================================== | |
| 52 | + | |
| 53 | + typedef struct _tOversampler | |
| 54 | + { | |
| 55 | + int ratio; | |
| 56 | + float* pCoeffs; | |
| 57 | + float* upState; | |
| 58 | + float* downState; | |
| 59 | + int numTaps; | |
| 60 | + int phaseLength; | |
| 61 | + } _tOversampler; | |
| 62 | + | |
| 63 | + typedef _tOversampler* tOversampler; | |
| 64 | + | |
| 65 | + void tOversampler_init (tOversampler* const, int order, oBool extraQuality); | |
| 66 | + void tOversampler_free (tOversampler* const); | |
| 67 | + void tOversampler_initToPool (tOversampler* const, int order, oBool extraQuality, tMempool* const); | |
| 68 | + void tOversampler_freeFromPool (tOversampler* const, tMempool* const); | |
| 69 | + | |
| 70 | + void tOversampler_upsample (tOversampler* const, float input, float* output); | |
| 71 | + float tOversampler_downsample (tOversampler* const os, float* input); | |
| 72 | + float tOversampler_tick (tOversampler* const, float input, float (*effectTick)(float)); | |
| 73 | + int tOversampler_getLatency (tOversampler* const os); | |
| 74 | + | |
| 75 | + //============================================================================== | |
| 76 | + | |
| 77 | + /* tLockhartWavefolder */ | |
| 78 | + | |
| 79 | + typedef struct _tLockhartWavefolder | |
| 80 | + { | |
| 81 | + double Ln1; | |
| 82 | + double Fn1; | |
| 83 | + double xn1; | |
| 84 | + | |
| 85 | + double RL; | |
| 86 | + double R; | |
| 87 | + double VT; | |
| 88 | + double Is; | |
| 89 | + | |
| 90 | + double a; | |
| 91 | + double b; | |
| 92 | + double d; | |
| 93 | + | |
| 94 | + // Antialiasing error threshold | |
| 95 | + double AAthresh; | |
| 96 | + double half_a; | |
| 97 | + double longthing; | |
| 98 | + | |
| 99 | + double LambertThresh, w, expw, p, r, s, myerr; | |
| 100 | + double l; | |
| 101 | + double u, Ln, Fn; | |
| 102 | + double tempsDenom; | |
| 103 | + double tempErrDenom; | |
| 104 | + double tempOutDenom; | |
| 105 | + | |
| 106 | + } _tLockhartWavefolder; | |
| 107 | + | |
| 108 | + typedef _tLockhartWavefolder* tLockhartWavefolder; | |
| 109 | + | |
| 110 | + void tLockhartWavefolder_init (tLockhartWavefolder* const); | |
| 111 | + void tLockhartWavefolder_free (tLockhartWavefolder* const); | |
| 112 | + void tLockhartWavefolder_initToPool (tLockhartWavefolder* const, tMempool* const); | |
| 113 | + void tLockhartWavefolder_freeFromPool (tLockhartWavefolder* const, tMempool* const); | |
| 114 | + | |
| 115 | + float tLockhartWavefolder_tick (tLockhartWavefolder* const, float samp); | |
| 116 | + | |
| 117 | + //============================================================================== | |
| 118 | + | |
| 119 | + typedef struct _tCrusher | |
| 120 | + { | |
| 121 | + float srr; | |
| 122 | + float mult, div; | |
| 123 | + float rnd; | |
| 124 | + | |
| 125 | + uint32_t op; //which bitwise operation (0-7) | |
| 126 | + | |
| 127 | + float gain; | |
| 128 | + tSampleReducer sReducer; | |
| 129 | + | |
| 130 | + } _tCrusher; | |
| 131 | + | |
| 132 | + typedef _tCrusher* tCrusher; | |
| 133 | + | |
| 134 | + void tCrusher_init (tCrusher* const); | |
| 135 | + void tCrusher_free (tCrusher* const); | |
| 136 | + void tCrusher_initToPool (tCrusher* const, tMempool* const); | |
| 137 | + void tCrusher_freeFromPool (tCrusher* const, tMempool* const); | |
| 138 | + | |
| 139 | + float tCrusher_tick (tCrusher* const, float input); | |
| 140 | + | |
| 141 | + // 0.0 - 1.0 | |
| 142 | + void tCrusher_setOperation (tCrusher* const, float op); | |
| 143 | + | |
| 144 | + // 0.0 - 1.0 | |
| 145 | + void tCrusher_setQuality (tCrusher* const, float val); | |
| 146 | + | |
| 147 | + // what division to round to | |
| 148 | + void tCrusher_setRound (tCrusher* const, float rnd); | |
| 149 | + | |
| 150 | + // sampling ratio | |
| 151 | + void tCrusher_setSamplingRatio (tCrusher* const, float ratio); | |
| 152 | + | |
| 153 | + //============================================================================== | |
| 154 | + | |
| 155 | + | |
| 156 | +#ifdef __cplusplus | |
| 157 | +} | |
| 158 | +#endif | |
| 159 | + | |
| 160 | +#endif // LEAF_DISTORTION_H_INCLUDED | |
| 161 | + | |
| 162 | +//============================================================================== | |
| 163 | + | |
| 164 | + | |
| @@ -0,0 +1,122 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-dynamics.h | |
| 4 | + Created: 30 Nov 2018 11:57:05am | |
| 5 | + Author: airship | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_DYNAMICS_H_INCLUDED | |
| 10 | +#define LEAF_DYNAMICS_H_INCLUDED | |
| 11 | + | |
| 12 | + | |
| 13 | +#ifdef __cplusplus | |
| 14 | +extern "C" { | |
| 15 | +#endif | |
| 16 | + | |
| 17 | + //============================================================================== | |
| 18 | + | |
| 19 | +#include "leaf-global.h" | |
| 20 | +#include "leaf-math.h" | |
| 21 | +#include "leaf-mempool.h" | |
| 22 | +#include "leaf-analysis.h" | |
| 23 | + | |
| 24 | + /*! | |
| 25 | + * @internal | |
| 26 | + * Header. | |
| 27 | + * @include basic-oscillators.h | |
| 28 | + * @example basic-oscillators.c | |
| 29 | + * An example. | |
| 30 | + */ | |
| 31 | + | |
| 32 | + //============================================================================== | |
| 33 | + | |
| 34 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 35 | + | |
| 36 | + /* Compressor */ | |
| 37 | + typedef struct _tCompressor | |
| 38 | + { | |
| 39 | + float tauAttack, tauRelease; | |
| 40 | + float T, R, W, M; // Threshold, compression Ratio, decibel Width of knee transition, decibel Make-up gain | |
| 41 | + | |
| 42 | + float x_G[2], y_G[2], x_T[2], y_T[2]; | |
| 43 | + | |
| 44 | + oBool isActive; | |
| 45 | + | |
| 46 | + } _tCompressor; | |
| 47 | + | |
| 48 | + typedef _tCompressor* tCompressor; | |
| 49 | + | |
| 50 | + void tCompressor_init (tCompressor* const); | |
| 51 | + void tCompressor_free (tCompressor* const); | |
| 52 | + void tCompressor_initToPool (tCompressor* const, tMempool* const); | |
| 53 | + void tCompressor_freeFromPool(tCompressor* const, tMempool* const); | |
| 54 | + | |
| 55 | + float tCompressor_tick (tCompressor* const, float input); | |
| 56 | + | |
| 57 | + /// | |
| 58 | + /* Feedback leveller */ | |
| 59 | + // An auto VCA that you put into a feedback circuit to make it stay at the same level. | |
| 60 | + // It can enforce level bidirectionally (amplifying and attenuating as needed) or | |
| 61 | + // just attenutating. The former option allows for infinite sustain strings, for example, while | |
| 62 | + // The latter option allows for decaying strings, which can never exceed | |
| 63 | + // a specific level. | |
| 64 | + | |
| 65 | + typedef struct _tFeedbackLeveler { | |
| 66 | + float targetLevel; // target power level | |
| 67 | + float strength; // how strongly level difference affects the VCA | |
| 68 | + int mode; // 0 for upwards limiting only, 1 for biderctional limiting | |
| 69 | + float curr; | |
| 70 | + tPowerFollower pwrFlw; // internal power follower needed for level tracking | |
| 71 | + | |
| 72 | + } _tFeedbackLeveler; | |
| 73 | + | |
| 74 | + typedef _tFeedbackLeveler* tFeedbackLeveler; | |
| 75 | + | |
| 76 | + void tFeedbackLeveler_init (tFeedbackLeveler* const, float targetLevel, float factor, float strength, int mode); | |
| 77 | + void tFeedbackLeveler_free (tFeedbackLeveler* const); | |
| 78 | + void tFeedbackLeveler_initToPool (tFeedbackLeveler* const, float targetLevel, float factor, float strength, int mode, tMempool* const); | |
| 79 | + void tFeedbackLeveler_freeFromPool (tFeedbackLeveler* const, tMempool* const); | |
| 80 | + | |
| 81 | + float tFeedbackLeveler_tick (tFeedbackLeveler* const, float input); | |
| 82 | + float tFeedbackLeveler_sample (tFeedbackLeveler* const); | |
| 83 | + void tFeedbackLeveler_setTargetLevel (tFeedbackLeveler* const, float TargetLevel); | |
| 84 | + void tFeedbackLeveler_setFactor (tFeedbackLeveler* const, float factor); | |
| 85 | + void tFeedbackLeveler_setMode (tFeedbackLeveler* const, int mode); // 0 for upwards limiting only, 1 for biderctional limiting | |
| 86 | + void tFeedbackLeveler_setStrength (tFeedbackLeveler* const, float strength); | |
| 87 | + | |
| 88 | + | |
| 89 | + //============================================================================== | |
| 90 | + | |
| 91 | + | |
| 92 | + //Threshold with hysteresis (like Max/MSP thresh~ object) | |
| 93 | + | |
| 94 | + typedef struct _tThreshold { | |
| 95 | + float highThresh, lowThresh; | |
| 96 | + int currentValue; | |
| 97 | + | |
| 98 | + } _tThreshold; | |
| 99 | + | |
| 100 | + typedef _tThreshold* tThreshold; | |
| 101 | + | |
| 102 | + void tThreshold_init (tThreshold* const, float low, float high); | |
| 103 | + void tThreshold_free (tThreshold* const); | |
| 104 | + void tThreshold_initToPool (tThreshold* const, float low, float high, tMempool* const); | |
| 105 | + void tThreshold_freeFromPool(tThreshold* const, tMempool* const); | |
| 106 | + | |
| 107 | + int tThreshold_tick (tThreshold* const, float input); | |
| 108 | + void tThreshold_setLow (tThreshold* const, float low); | |
| 109 | + void tThreshold_setHigh (tThreshold* const, float high); | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + //////====================================================================== | |
| 114 | + | |
| 115 | +#ifdef __cplusplus | |
| 116 | +} | |
| 117 | +#endif | |
| 118 | + | |
| 119 | +#endif // LEAF_DYNAMICS_H_INCLUDED | |
| 120 | + | |
| 121 | +//============================================================================== | |
| 122 | + | |
| @@ -0,0 +1,360 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-effects.h | |
| 4 | + Created: 20 Jan 2017 12:01:54pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_EFFECTS_H_INCLUDED | |
| 10 | +#define LEAF_EFFECTS_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | + //============================================================================== | |
| 17 | +#include "leaf-global.h" | |
| 18 | +#include "leaf-math.h" | |
| 19 | +#include "leaf-mempool.h" | |
| 20 | +#include "leaf-dynamics.h" | |
| 21 | +#include "leaf-analysis.h" | |
| 22 | +#include "leaf-envelopes.h" | |
| 23 | + | |
| 24 | + /*! | |
| 25 | + * @internal | |
| 26 | + * Header. | |
| 27 | + * @include basic-oscillators.h | |
| 28 | + * @example basic-oscillators.c | |
| 29 | + * An example. | |
| 30 | + */ | |
| 31 | + | |
| 32 | + //============================================================================== | |
| 33 | + | |
| 34 | + /* tTalkbox */ | |
| 35 | +#define NUM_TALKBOX_PARAM 4 | |
| 36 | + | |
| 37 | + typedef struct _tTalkbox | |
| 38 | + { | |
| 39 | + float param[NUM_TALKBOX_PARAM]; | |
| 40 | + | |
| 41 | + int bufsize; | |
| 42 | + float* car0; | |
| 43 | + float* car1; | |
| 44 | + float* window; | |
| 45 | + float* buf0; | |
| 46 | + float* buf1; | |
| 47 | + | |
| 48 | + float warpFactor; | |
| 49 | + int32_t warpOn; | |
| 50 | + | |
| 51 | + float emphasis; | |
| 52 | + int32_t K, N, O, pos; | |
| 53 | + float wet, dry, FX; | |
| 54 | + float d0, d1, d2, d3, d4; | |
| 55 | + float u0, u1, u2, u3, u4; | |
| 56 | + | |
| 57 | + | |
| 58 | + } _tTalkbox; | |
| 59 | + | |
| 60 | + typedef _tTalkbox* tTalkbox; | |
| 61 | + | |
| 62 | + void tTalkbox_init (tTalkbox* const, int bufsize); | |
| 63 | + void tTalkbox_free (tTalkbox* const); | |
| 64 | + void tTalkbox_initToPool (tTalkbox* const, int bufsize, tMempool* const); | |
| 65 | + void tTalkbox_freeFromPool (tTalkbox* const, tMempool* const); | |
| 66 | + | |
| 67 | + float tTalkbox_tick (tTalkbox* const, float synth, float voice); | |
| 68 | + void tTalkbox_update (tTalkbox* const); | |
| 69 | + void tTalkbox_suspend (tTalkbox* const); | |
| 70 | + void tTalkbox_lpcDurbin (float *r, int p, float *k, float *g); | |
| 71 | + void tTalkbox_lpc (float *buf, float *car, int32_t n, int32_t o, float warp, int warpOn); | |
| 72 | + void tTalkbox_setQuality (tTalkbox* const, float quality); | |
| 73 | + void tTalkbox_setWarpFactor (tTalkbox* const voc, float warp); | |
| 74 | + void tTalkbox_setWarpOn (tTalkbox* const voc, float warpOn); | |
| 75 | + void tTalkbox_warpedAutocorrelate (float * x, unsigned int L, float * R, unsigned int P, float lambda); | |
| 76 | + //============================================================================== | |
| 77 | + | |
| 78 | + /* tVocoder */ | |
| 79 | +#define NUM_VOCODER_PARAM 8 | |
| 80 | +#define NBANDS 16 | |
| 81 | + | |
| 82 | + typedef struct _tVocoder | |
| 83 | + { | |
| 84 | + float param[NUM_VOCODER_PARAM]; | |
| 85 | + | |
| 86 | + float gain; //output level | |
| 87 | + float thru, high; //hf thru | |
| 88 | + float kout; //downsampled output | |
| 89 | + int32_t kval; //downsample counter | |
| 90 | + int32_t nbnd; //number of bands | |
| 91 | + | |
| 92 | + //filter coeffs and buffers - seems it's faster to leave this global than make local copy | |
| 93 | + float f[NBANDS][13]; //[0-8][0 1 2 | 0 1 2 3 | 0 1 2 3 | val rate] | |
| 94 | + | |
| 95 | + } _tVocoder; | |
| 96 | + | |
| 97 | + typedef _tVocoder* tVocoder; | |
| 98 | + | |
| 99 | + void tVocoder_init (tVocoder* const); | |
| 100 | + void tVocoder_free (tVocoder* const); | |
| 101 | + void tVocoder_initToPool (tVocoder* const, tMempool* const); | |
| 102 | + void tVocoder_freeFromPool (tVocoder* const, tMempool* const); | |
| 103 | + | |
| 104 | + float tVocoder_tick (tVocoder* const, float synth, float voice); | |
| 105 | + void tVocoder_update (tVocoder* const); | |
| 106 | + void tVocoder_suspend (tVocoder* const); | |
| 107 | + | |
| 108 | + //============================================================================== | |
| 109 | + | |
| 110 | + // tRosenbergGlottalPulse | |
| 111 | + | |
| 112 | + typedef struct _tRosenbergGlottalPulse | |
| 113 | + { | |
| 114 | + | |
| 115 | + float phase; | |
| 116 | + float openLength; | |
| 117 | + float pulseLength; | |
| 118 | + float freq; | |
| 119 | + float inc; | |
| 120 | + | |
| 121 | + | |
| 122 | + } _tRosenbergGlottalPulse; | |
| 123 | + | |
| 124 | + typedef _tRosenbergGlottalPulse* tRosenbergGlottalPulse; | |
| 125 | + | |
| 126 | + void tRosenbergGlottalPulse_init (tRosenbergGlottalPulse* const); | |
| 127 | + void tRosenbergGlottalPulse_free (tRosenbergGlottalPulse* const); | |
| 128 | + void tRosenbergGlottalPulse_initToPool (tRosenbergGlottalPulse* const, tMempool* const); | |
| 129 | + void tRosenbergGlottalPulse_freeFromPool (tRosenbergGlottalPulse* const, tMempool* const); | |
| 130 | + | |
| 131 | + float tRosenbergGlottalPulse_tick (tRosenbergGlottalPulse* const); | |
| 132 | + | |
| 133 | + void tRosenbergGlottalPulse_setFreq (tRosenbergGlottalPulse* const, float freq); | |
| 134 | + | |
| 135 | + void tRosenbergGlottalPulse_setOpenLength (tRosenbergGlottalPulse* const, float openLength); | |
| 136 | + | |
| 137 | + void tRosenbergGlottalPulse_setPulseLength (tRosenbergGlottalPulse* const, float pulseLength); | |
| 138 | + | |
| 139 | + //============================================================================== | |
| 140 | + | |
| 141 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 142 | + | |
| 143 | + /* tSOLAD : pitch shifting algorithm that underlies tRetune etc */ | |
| 144 | + // from Katja Vetters http://www.katjaas.nl/pitchshiftlowlatency/pitchshiftlowlatency.html | |
| 145 | +#define LOOPSIZE (2048*2) // (4096*2) // loop size must be power of two | |
| 146 | +#define LOOPMASK (LOOPSIZE - 1) | |
| 147 | +#define PITCHFACTORDEFAULT 1.0f | |
| 148 | +#define INITPERIOD 64.0f | |
| 149 | +#define MAXPERIOD (float)((LOOPSIZE - w->blocksize) * 0.8f) | |
| 150 | +#define MINPERIOD 8.0f | |
| 151 | + | |
| 152 | + typedef struct _tSOLAD | |
| 153 | + { | |
| 154 | + uint16_t timeindex; // current reference time, write index | |
| 155 | + uint16_t blocksize; // signal input / output block size | |
| 156 | + float pitchfactor; // pitch factor between 0.25 and 4 | |
| 157 | + float readlag; // read pointer's lag behind write pointer | |
| 158 | + float period; // period length in input signal | |
| 159 | + float jump; // read pointer jump length and direction | |
| 160 | + float xfadelength; // crossfade length expressed at input sample rate | |
| 161 | + float xfadevalue; // crossfade phase and value | |
| 162 | + | |
| 163 | + float* delaybuf; | |
| 164 | + | |
| 165 | + } _tSOLAD; | |
| 166 | + | |
| 167 | + typedef _tSOLAD* tSOLAD; | |
| 168 | + | |
| 169 | + void tSOLAD_init (tSOLAD* const); | |
| 170 | + void tSOLAD_free (tSOLAD* const); | |
| 171 | + void tSOLAD_initToPool (tSOLAD* const, tMempool* const); | |
| 172 | + void tSOLAD_freeFromPool (tSOLAD* const, tMempool* const); | |
| 173 | + | |
| 174 | + // send one block of input samples, receive one block of output samples | |
| 175 | + void tSOLAD_ioSamples (tSOLAD *w, float* in, float* out, int blocksize); | |
| 176 | + // set periodicity analysis data | |
| 177 | + void tSOLAD_setPeriod (tSOLAD *w, float period); | |
| 178 | + // set pitch factor between 0.25 and 4 | |
| 179 | + void tSOLAD_setPitchFactor (tSOLAD *w, float pitchfactor); | |
| 180 | + // force readpointer lag | |
| 181 | + void tSOLAD_setReadLag (tSOLAD *w, float readlag); | |
| 182 | + // reset state variables | |
| 183 | + void tSOLAD_resetState (tSOLAD *w); | |
| 184 | + | |
| 185 | + // Pitch shift | |
| 186 | + typedef struct _tPitchShift | |
| 187 | + { | |
| 188 | + tSOLAD sola; | |
| 189 | + tHighpass hp; | |
| 190 | + tPeriodDetection* p; | |
| 191 | + | |
| 192 | + float* outBuffer; | |
| 193 | + int frameSize; | |
| 194 | + int bufSize; | |
| 195 | + | |
| 196 | + int framesPerBuffer; | |
| 197 | + int curBlock; | |
| 198 | + int lastBlock; | |
| 199 | + int index; | |
| 200 | + | |
| 201 | + float pitchFactor; | |
| 202 | + float timeConstant; | |
| 203 | + float radius; | |
| 204 | + } _tPitchShift; | |
| 205 | + | |
| 206 | + typedef _tPitchShift* tPitchShift; | |
| 207 | + | |
| 208 | + void tPitchShift_init (tPitchShift* const, tPeriodDetection* const, float* out, int bufSize); | |
| 209 | + void tPitchShift_free (tPitchShift* const); | |
| 210 | + void tPitchShift_initToPool (tPitchShift* const, tPeriodDetection* const, float* out, int bufSize, tMempool* const); | |
| 211 | + void tPitchShift_freeFromPool (tPitchShift* const, tMempool* const); | |
| 212 | + | |
| 213 | + float tPitchShift_shift (tPitchShift* const); | |
| 214 | + float tPitchShift_shiftToFunc (tPitchShift* const, float (*fun)(float)); | |
| 215 | + float tPitchShift_shiftToFreq (tPitchShift* const, float freq); | |
| 216 | + void tPitchShift_setPitchFactor (tPitchShift* const, float pf); | |
| 217 | + | |
| 218 | + // Retune | |
| 219 | + typedef struct _tRetune | |
| 220 | + { | |
| 221 | + tPeriodDetection pd; | |
| 222 | + tPitchShift* ps; | |
| 223 | + | |
| 224 | + float* inBuffer; | |
| 225 | + float** outBuffers; | |
| 226 | + float* tickOutput; | |
| 227 | + int frameSize; | |
| 228 | + int bufSize; | |
| 229 | + | |
| 230 | + uint16_t hopSize; | |
| 231 | + uint16_t windowSize; | |
| 232 | + uint8_t fba; | |
| 233 | + | |
| 234 | + float* pitchFactor; | |
| 235 | + float timeConstant; | |
| 236 | + float radius; | |
| 237 | + | |
| 238 | + float inputPeriod; | |
| 239 | + | |
| 240 | + int numVoices; | |
| 241 | + } _tRetune; | |
| 242 | + | |
| 243 | + typedef _tRetune* tRetune; | |
| 244 | + | |
| 245 | + void tRetune_init (tRetune* const, int numVoices, int bufSize, int frameSize); | |
| 246 | + void tRetune_free (tRetune* const); | |
| 247 | + void tRetune_initToPool (tRetune* const, int numVoices, int bufSize, int frameSize, tMempool* const); | |
| 248 | + void tRetune_freeFromPool (tRetune* const, tMempool* const); | |
| 249 | + | |
| 250 | + float* tRetune_tick (tRetune* const, float sample); | |
| 251 | + void tRetune_setNumVoices (tRetune* const, int numVoices); | |
| 252 | + void tRetune_setPitchFactors (tRetune* const, float pf); | |
| 253 | + void tRetune_setPitchFactor (tRetune* const, float pf, int voice); | |
| 254 | + void tRetune_setTimeConstant (tRetune* const, float tc); | |
| 255 | + void tRetune_setHopSize (tRetune* const, int hs); | |
| 256 | + void tRetune_setWindowSize (tRetune* const, int ws); | |
| 257 | + void tRetune_setFidelityThreshold(tRetune* const, float threshold); | |
| 258 | + float tRetune_getInputPeriod (tRetune* const); | |
| 259 | + float tRetune_getInputFreq (tRetune* const); | |
| 260 | + | |
| 261 | + // Autotune | |
| 262 | + typedef struct _tAutotune | |
| 263 | + { | |
| 264 | + tPeriodDetection pd; | |
| 265 | + tPitchShift* ps; | |
| 266 | + | |
| 267 | + float* inBuffer; | |
| 268 | + float** outBuffers; | |
| 269 | + float* tickOutput; | |
| 270 | + int frameSize; | |
| 271 | + int bufSize; | |
| 272 | + | |
| 273 | + uint16_t hopSize; | |
| 274 | + uint16_t windowSize; | |
| 275 | + uint8_t fba; | |
| 276 | + | |
| 277 | + float* freq; | |
| 278 | + float timeConstant; | |
| 279 | + float radius; | |
| 280 | + | |
| 281 | + float inputPeriod; | |
| 282 | + | |
| 283 | + int numVoices; | |
| 284 | + } _tAutotune; | |
| 285 | + | |
| 286 | + typedef _tAutotune* tAutotune; | |
| 287 | + | |
| 288 | + void tAutotune_init (tAutotune* const, int numVoices, int bufSize, int frameSize); | |
| 289 | + void tAutotune_free (tAutotune* const); | |
| 290 | + void tAutotune_initToPool (tAutotune* const, int numVoices, int bufSize, int frameSize, tMempool* const); | |
| 291 | + void tAutotune_freeFromPool (tAutotune* const, tMempool* const); | |
| 292 | + | |
| 293 | + float* tAutotune_tick (tAutotune* const, float sample); | |
| 294 | + void tAutotune_setNumVoices (tAutotune* const, int numVoices); | |
| 295 | + void tAutotune_setFreqs (tAutotune* const, float f); | |
| 296 | + void tAutotune_setFreq (tAutotune* const, float f, int voice); | |
| 297 | + void tAutotune_setTimeConstant (tAutotune* const, float tc); | |
| 298 | + void tAutotune_setHopSize (tAutotune* const, int hs); | |
| 299 | + void tAutotune_setWindowSize (tAutotune* const, int ws); | |
| 300 | + void tAutotune_setFidelityThreshold (tAutotune* const, float threshold); | |
| 301 | + void tAutotune_setAlpha (tAutotune* const, float alpha); | |
| 302 | + void tAutotune_setTolerance (tAutotune* const, float tolerance); | |
| 303 | + float tAutotune_getInputPeriod (tAutotune* const); | |
| 304 | + float tAutotune_getInputFreq (tAutotune* const); | |
| 305 | + | |
| 306 | + //============================================================================== | |
| 307 | + | |
| 308 | + | |
| 309 | + typedef struct _tFormantShifter | |
| 310 | + { | |
| 311 | + int ford; | |
| 312 | + float falph; | |
| 313 | + float flamb; | |
| 314 | + float* fk; | |
| 315 | + float* fb; | |
| 316 | + float* fc; | |
| 317 | + float* frb; | |
| 318 | + float* frc; | |
| 319 | + float* fsig; | |
| 320 | + float* fsmooth; | |
| 321 | + float fhp; | |
| 322 | + float flp; | |
| 323 | + float flpa; | |
| 324 | + float* fbuff; | |
| 325 | + float* ftvec; | |
| 326 | + float fmute; | |
| 327 | + float fmutealph; | |
| 328 | + unsigned int cbi; | |
| 329 | + float shiftFactor; | |
| 330 | + float intensity, invIntensity; | |
| 331 | + tHighpass hp; | |
| 332 | + tHighpass hp2; | |
| 333 | + tFeedbackLeveler fbl1; | |
| 334 | + tFeedbackLeveler fbl2; | |
| 335 | + | |
| 336 | + } _tFormantShifter; | |
| 337 | + | |
| 338 | + typedef _tFormantShifter* tFormantShifter; | |
| 339 | + | |
| 340 | + void tFormantShifter_init (tFormantShifter* const, int order); | |
| 341 | + void tFormantShifter_free (tFormantShifter* const); | |
| 342 | + void tFormantShifter_initToPool (tFormantShifter* const, int order, tMempool* const); | |
| 343 | + void tFormantShifter_freeFromPool (tFormantShifter* const, tMempool* const); | |
| 344 | + | |
| 345 | + float tFormantShifter_tick (tFormantShifter* const, float input); | |
| 346 | + float tFormantShifter_remove (tFormantShifter* const, float input); | |
| 347 | + float tFormantShifter_add (tFormantShifter* const, float input); | |
| 348 | + void tFormantShifter_ioSamples (tFormantShifter* const, float* in, float* out, int size, float fwarp); | |
| 349 | + void tFormantShifter_setShiftFactor (tFormantShifter* const, float shiftFactor); | |
| 350 | + void tFormantShifter_setIntensity (tFormantShifter* const, float intensity); | |
| 351 | + | |
| 352 | + //============================================================================== | |
| 353 | + | |
| 354 | +#ifdef __cplusplus | |
| 355 | +} | |
| 356 | +#endif | |
| 357 | + | |
| 358 | +#endif // LEAF_EFFECTS_H_INCLUDED | |
| 359 | + | |
| 360 | +//============================================================================== | |
| @@ -0,0 +1,103 @@ | ||
| 1 | +/* | |
| 2 | + * leaf-electrical.h | |
| 3 | + * | |
| 4 | + * Created on: Sep 25, 2019 | |
| 5 | + * Author: jeffsnyder | |
| 6 | + */ | |
| 7 | + | |
| 8 | +#ifndef LEAF_INC_LEAF_ELECTRICAL_H_ | |
| 9 | +#define LEAF_INC_LEAF_ELECTRICAL_H_ | |
| 10 | + | |
| 11 | +#ifdef __cplusplus | |
| 12 | +extern "C" { | |
| 13 | +#endif | |
| 14 | + | |
| 15 | + //============================================================================== | |
| 16 | + | |
| 17 | +#include "leaf-global.h" | |
| 18 | +#include "leaf-math.h" | |
| 19 | +#include "leaf-mempool.h" | |
| 20 | + | |
| 21 | + /*! | |
| 22 | + * @internal | |
| 23 | + * Header. | |
| 24 | + * @include basic-oscillators.h | |
| 25 | + * @example basic-oscillators.c | |
| 26 | + * An example. | |
| 27 | + */ | |
| 28 | + | |
| 29 | + //============================================================================== | |
| 30 | + | |
| 31 | + typedef enum WDFComponentType | |
| 32 | + { | |
| 33 | + SeriesAdaptor = 0, | |
| 34 | + ParallelAdaptor, | |
| 35 | + Resistor, | |
| 36 | + Capacitor, | |
| 37 | + Inductor, | |
| 38 | + Inverter, | |
| 39 | + ResistiveSource, | |
| 40 | + IdealSource, | |
| 41 | + Diode, | |
| 42 | + DiodePair, | |
| 43 | + RootNil, | |
| 44 | + WDFComponentNil | |
| 45 | + } WDFComponentType; | |
| 46 | + | |
| 47 | + typedef struct _tWDF _tWDF; // needed to allow tWDF pointers in struct | |
| 48 | + typedef _tWDF* tWDF; | |
| 49 | + struct _tWDF | |
| 50 | + { | |
| 51 | + WDFComponentType type; | |
| 52 | + float port_resistance_up; | |
| 53 | + float port_resistance_left; | |
| 54 | + float port_resistance_right; | |
| 55 | + float port_conductance_up; | |
| 56 | + float port_conductance_left; | |
| 57 | + float port_conductance_right; | |
| 58 | + float incident_wave_up; | |
| 59 | + float incident_wave_left; | |
| 60 | + float incident_wave_right; | |
| 61 | + float reflected_wave_up; | |
| 62 | + float reflected_wave_left; | |
| 63 | + float reflected_wave_right; | |
| 64 | + float gamma_zero; | |
| 65 | + float sample_rate; | |
| 66 | + float value; | |
| 67 | + tWDF* child_left; | |
| 68 | + tWDF* child_right; | |
| 69 | + float (*get_port_resistance)(tWDF* const); | |
| 70 | + float (*get_reflected_wave_up)(tWDF* const, float); | |
| 71 | + float (*get_reflected_wave_down)(tWDF* const, float, float); | |
| 72 | + void (*set_incident_wave)(tWDF* const, float, float); | |
| 73 | + }; | |
| 74 | + | |
| 75 | + //WDF Linear Components | |
| 76 | + void tWDF_init (tWDF* const, WDFComponentType type, float value, tWDF* const rL, tWDF* const rR); | |
| 77 | + void tWDF_free (tWDF* const); | |
| 78 | + void tWDF_initToPool (tWDF* const, WDFComponentType type, float value, tWDF* const rL, tWDF* const rR, tMempool* const); | |
| 79 | + void tWDF_freeFromPool (tWDF* const, tMempool* const); | |
| 80 | + | |
| 81 | + float tWDF_tick (tWDF* const, float sample, tWDF* const outputPoint, uint8_t paramsChanged); | |
| 82 | + | |
| 83 | + void tWDF_setValue (tWDF* const, float value); | |
| 84 | + void tWDF_setSampleRate (tWDF* const, float sample_rate); | |
| 85 | + uint8_t tWDF_isLeaf (tWDF* const); | |
| 86 | + | |
| 87 | + float tWDF_getPortResistance (tWDF* const); | |
| 88 | + float tWDF_getReflectedWaveUp (tWDF* const, float input); //for tree, only uses input for resistive source | |
| 89 | + float tWDF_getReflectedWaveDown (tWDF* const, float input, float incident_wave); //for roots | |
| 90 | + void tWDF_setIncidentWave (tWDF* const, float incident_wave, float input); | |
| 91 | + | |
| 92 | + float tWDF_getVoltage (tWDF* const); | |
| 93 | + float tWDF_getCurrent (tWDF* const); | |
| 94 | + | |
| 95 | + | |
| 96 | + //============================================================================== | |
| 97 | + | |
| 98 | +#ifdef __cplusplus | |
| 99 | +} | |
| 100 | +#endif | |
| 101 | + | |
| 102 | +#endif /* LEAF_INC_LEAF_ELECTRICAL_H_ */ | |
| 103 | + | |
| @@ -0,0 +1,366 @@ | ||
| 1 | +/* | |
| 2 | + ============================================================================== | |
| 3 | + | |
| 4 | + leaf-envelopes.h | |
| 5 | + Created: 20 Jan 2017 12:02:17pm | |
| 6 | + Author: Michael R Mulshine | |
| 7 | + | |
| 8 | + ============================================================================== | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#ifndef LEAF_ENVELOPES_H_INCLUDED | |
| 12 | +#define LEAF_ENVELOPES_H_INCLUDED | |
| 13 | + | |
| 14 | +#ifdef __cplusplus | |
| 15 | +extern "C" { | |
| 16 | +#endif | |
| 17 | + | |
| 18 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 19 | + | |
| 20 | +#include "leaf-math.h" | |
| 21 | +#include "leaf-mempool.h" | |
| 22 | +#include "leaf-filters.h" | |
| 23 | +#include "leaf-delay.h" | |
| 24 | +#include "leaf-analysis.h" | |
| 25 | +#include "leaf-envelopes.h" | |
| 26 | + | |
| 27 | + /*! | |
| 28 | + * @internal | |
| 29 | + * Header. | |
| 30 | + * @include basic-oscillators.h | |
| 31 | + * @example basic-oscillators.c | |
| 32 | + * An example. | |
| 33 | + */ | |
| 34 | + | |
| 35 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 36 | + | |
| 37 | + /* Attack-Decay envelope */ | |
| 38 | + typedef struct _tEnvelope { | |
| 39 | + | |
| 40 | + const float *exp_buff; | |
| 41 | + const float *inc_buff; | |
| 42 | + uint32_t buff_size; | |
| 43 | + | |
| 44 | + float next; | |
| 45 | + | |
| 46 | + float attackInc, decayInc, rampInc; | |
| 47 | + | |
| 48 | + int inAttack, inDecay, inRamp; | |
| 49 | + | |
| 50 | + int loop; | |
| 51 | + | |
| 52 | + float gain, rampPeak; | |
| 53 | + | |
| 54 | + float attackPhase, decayPhase, rampPhase; | |
| 55 | + | |
| 56 | + } _tEnvelope; | |
| 57 | + | |
| 58 | + typedef _tEnvelope* tEnvelope; | |
| 59 | + | |
| 60 | + void tEnvelope_init (tEnvelope* const, float attack, float decay, int loop); | |
| 61 | + void tEnvelope_free (tEnvelope* const); | |
| 62 | + void tEnvelope_initToPool (tEnvelope* const, float attack, float decay, int loop, tMempool* const); | |
| 63 | + void tEnvelope_freeFromPool (tEnvelope* const, tMempool* const); | |
| 64 | + | |
| 65 | + float tEnvelope_tick (tEnvelope* const); | |
| 66 | + void tEnvelope_setAttack (tEnvelope* const, float attack); | |
| 67 | + void tEnvelope_setDecay (tEnvelope* const, float decay); | |
| 68 | + void tEnvelope_loop (tEnvelope* const, int loop); | |
| 69 | + void tEnvelope_on (tEnvelope* const, float velocity); | |
| 70 | + | |
| 71 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + /* Exponential Smoother */ | |
| 76 | + typedef struct _tExpSmooth { | |
| 77 | + float factor, oneminusfactor; | |
| 78 | + float curr,dest; | |
| 79 | + | |
| 80 | + } _tExpSmooth; | |
| 81 | + | |
| 82 | + typedef _tExpSmooth* tExpSmooth; | |
| 83 | + | |
| 84 | + void tExpSmooth_init (tExpSmooth* const, float val, float factor); | |
| 85 | + void tExpSmooth_free (tExpSmooth* const); | |
| 86 | + void tExpSmooth_initToPool (tExpSmooth* const, float val, float factor, tMempool* const); | |
| 87 | + void tExpSmooth_freeFromPool (tExpSmooth* const, tMempool* const); | |
| 88 | + | |
| 89 | + float tExpSmooth_tick (tExpSmooth* const); | |
| 90 | + float tExpSmooth_sample (tExpSmooth* const); | |
| 91 | + void tExpSmooth_setFactor (tExpSmooth* const, float factor); | |
| 92 | + void tExpSmooth_setDest (tExpSmooth* const, float dest); | |
| 93 | + void tExpSmooth_setVal (tExpSmooth* const, float val); | |
| 94 | + void tExpSmooth_setValAndDest(tExpSmooth* const expsmooth, float val); | |
| 95 | + | |
| 96 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 97 | + | |
| 98 | + | |
| 99 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 100 | + | |
| 101 | + | |
| 102 | + /* ADSR */ | |
| 103 | + typedef struct _tADSR | |
| 104 | + { | |
| 105 | + const float *exp_buff; | |
| 106 | + const float *inc_buff; | |
| 107 | + uint32_t buff_size; | |
| 108 | + | |
| 109 | + float next; | |
| 110 | + | |
| 111 | + float attackInc, decayInc, releaseInc, rampInc; | |
| 112 | + | |
| 113 | + oBool inAttack, inDecay, inSustain, inRelease, inRamp; | |
| 114 | + | |
| 115 | + float sustain, gain, rampPeak, releasePeak; | |
| 116 | + | |
| 117 | + float attackPhase, decayPhase, releasePhase, rampPhase; | |
| 118 | + | |
| 119 | + float leakFactor; | |
| 120 | + | |
| 121 | + | |
| 122 | + } _tADSR; | |
| 123 | + | |
| 124 | + typedef _tADSR* tADSR; | |
| 125 | + | |
| 126 | + void tADSR_init (tADSR* const adsrenv, float attack, float decay, float sustain, float release); | |
| 127 | + void tADSR_free (tADSR* const); | |
| 128 | + void tADSR_initToPool (tADSR* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp); | |
| 129 | + void tADSR_freeFromPool (tADSR* const, tMempool* const); | |
| 130 | + | |
| 131 | + float tADSR_tick (tADSR* const); | |
| 132 | + void tADSR_setAttack (tADSR* const, float attack); | |
| 133 | + void tADSR_setDecay (tADSR* const, float decay); | |
| 134 | + void tADSR_setSustain (tADSR* const, float sustain); | |
| 135 | + void tADSR_setRelease (tADSR* const, float release); | |
| 136 | + void tADSR_setLeakFactor (tADSR* const, float leakFactor); | |
| 137 | + void tADSR_on (tADSR* const, float velocity); | |
| 138 | + void tADSR_off (tADSR* const); | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + /* ADSR2 */ | |
| 148 | + typedef struct _tADSR2 | |
| 149 | + { | |
| 150 | + float sampleRateInMs; | |
| 151 | + float attack; | |
| 152 | + float decay; | |
| 153 | + float release; | |
| 154 | + float attackLambda; | |
| 155 | + float decayLambda; | |
| 156 | + float releaseLambda; | |
| 157 | + float sustain; | |
| 158 | + float leakGain; | |
| 159 | + float leakFactor; | |
| 160 | + float targetGainSquared; | |
| 161 | + float factor; | |
| 162 | + float oneMinusFactor; | |
| 163 | + float gain; | |
| 164 | + uint8_t attacking; | |
| 165 | + uint8_t gate; | |
| 166 | + float env; | |
| 167 | + float envTarget; | |
| 168 | + } _tADSR2; | |
| 169 | + | |
| 170 | + typedef _tADSR2* tADSR2; | |
| 171 | + | |
| 172 | + void tADSR2_init (tADSR2* const, float attack, float decay, float sustain, float release); | |
| 173 | + void tADSR2_free (tADSR2* const); | |
| 174 | + void tADSR2_initToPool (tADSR2* const, float attack, float decay, float sustain, float release, tMempool* const); | |
| 175 | + void tADSR2_freeFromPool (tADSR2* const, tMempool* const); | |
| 176 | + | |
| 177 | + float tADSR2_tick (tADSR2* const); | |
| 178 | + void tADSR2_setAttack (tADSR2* const, float attack); | |
| 179 | + void tADSR2_setDecay (tADSR2* const, float decay); | |
| 180 | + void tADSR2_setSustain (tADSR2* const, float sustain); | |
| 181 | + void tADSR2_setRelease (tADSR2* const, float release); | |
| 182 | + void tADSR2_setLeakFactor (tADSR2* const, float leakFactor); | |
| 183 | + void tADSR2_on (tADSR2* const, float velocity); | |
| 184 | + void tADSR2_off (tADSR2* const); | |
| 185 | + | |
| 186 | + | |
| 187 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 188 | + | |
| 189 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 190 | + enum envState { | |
| 191 | + env_idle = 0, | |
| 192 | + env_attack, | |
| 193 | + env_decay, | |
| 194 | + env_sustain, | |
| 195 | + env_release, | |
| 196 | + env_ramp | |
| 197 | + }; | |
| 198 | + | |
| 199 | + /* ADSR3 */ | |
| 200 | + typedef struct _tADSR3 | |
| 201 | + { | |
| 202 | + float sampleRateInMs; | |
| 203 | + int state; | |
| 204 | + float output; | |
| 205 | + float attackRate; | |
| 206 | + float decayRate; | |
| 207 | + float releaseRate; | |
| 208 | + float attackCoef; | |
| 209 | + float decayCoef; | |
| 210 | + float releaseCoef; | |
| 211 | + float sustainLevel; | |
| 212 | + float targetRatioA; | |
| 213 | + float targetRatioDR; | |
| 214 | + float attackBase; | |
| 215 | + float decayBase; | |
| 216 | + float releaseBase; | |
| 217 | + float leakFactor; | |
| 218 | + float targetGainSquared; | |
| 219 | + float factor; | |
| 220 | + float oneMinusFactor; | |
| 221 | + float gain; | |
| 222 | + | |
| 223 | + } _tADSR3; | |
| 224 | + | |
| 225 | + typedef _tADSR3* tADSR3; | |
| 226 | + | |
| 227 | + void tADSR3_init (tADSR3* const, float attack, float decay, float sustain, float release); | |
| 228 | + void tADSR3_free (tADSR3* const); | |
| 229 | + void tADSR3_initToPool (tADSR3* const, float attack, float decay, float sustain, float release, tMempool* const); | |
| 230 | + void tADSR3_freeFromPool (tADSR3* const, tMempool* const); | |
| 231 | + | |
| 232 | + float tADSR3_tick (tADSR3* const); | |
| 233 | + void tADSR3_setAttack (tADSR3* const, float attack); | |
| 234 | + void tADSR3_setDecay (tADSR3* const, float decay); | |
| 235 | + void tADSR3_setSustain (tADSR3* const, float sustain); | |
| 236 | + void tADSR3_setRelease (tADSR3* const, float release); | |
| 237 | + void tADSR3_setLeakFactor (tADSR3* const, float leakFactor); | |
| 238 | + void tADSR3_on (tADSR3* const, float velocity); | |
| 239 | + void tADSR3_off (tADSR3* const); | |
| 240 | + | |
| 241 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 242 | + | |
| 243 | + //ADSR4 | |
| 244 | + | |
| 245 | + | |
| 246 | + typedef struct _tADSR4 | |
| 247 | + { | |
| 248 | + const float *exp_buff; | |
| 249 | + uint32_t buff_size; | |
| 250 | + uint32_t buff_sizeMinusOne; | |
| 251 | + float bufferSizeDividedBySampleRateInMs; | |
| 252 | + float next; | |
| 253 | + | |
| 254 | + float attackInc, decayInc, releaseInc, rampInc; | |
| 255 | + | |
| 256 | + uint32_t whichStage; | |
| 257 | + | |
| 258 | + float sustain, gain, rampPeak, releasePeak; | |
| 259 | + | |
| 260 | + float attackPhase, decayPhase, releasePhase, rampPhase; | |
| 261 | + | |
| 262 | + float leakFactor; | |
| 263 | + | |
| 264 | + | |
| 265 | + } _tADSR4; | |
| 266 | + | |
| 267 | + typedef _tADSR4* tADSR4; | |
| 268 | + | |
| 269 | + void tADSR4_init (tADSR4* const, float attack, float decay, float sustain, float release, float* expBuffer, int bufferSize); | |
| 270 | + void tADSR4_free (tADSR4* const); | |
| 271 | + void tADSR4_initToPool (tADSR4* const, float attack, float decay, float sustain, float release, float* expBuffer, int bufferSize, tMempool* const); | |
| 272 | + void tADSR4_freeFromPool (tADSR4* const, tMempool* const); | |
| 273 | + | |
| 274 | + float tADSR4_tick (tADSR4* const); | |
| 275 | + float tADSR4_tickNoInterp (tADSR4* const adsrenv); | |
| 276 | + void tADSR4_setAttack (tADSR4* const, float attack); | |
| 277 | + void tADSR4_setDecay (tADSR4* const, float decay); | |
| 278 | + void tADSR4_setSustain (tADSR4* const, float sustain); | |
| 279 | + void tADSR4_setRelease (tADSR4* const, float release); | |
| 280 | + void tADSR4_setLeakFactor (tADSR4* const, float leakFactor); | |
| 281 | + void tADSR4_on (tADSR4* const, float velocity); | |
| 282 | + void tADSR4_off (tADSR4* const); | |
| 283 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 284 | + | |
| 285 | + /* Ramp */ | |
| 286 | + typedef struct _tRamp { | |
| 287 | + float inc; | |
| 288 | + float inv_sr_ms; | |
| 289 | + float minimum_time; | |
| 290 | + float curr,dest; | |
| 291 | + float time; | |
| 292 | + float factor; | |
| 293 | + int samples_per_tick; | |
| 294 | + | |
| 295 | + } _tRamp; | |
| 296 | + | |
| 297 | + typedef _tRamp* tRamp; | |
| 298 | + | |
| 299 | + void tRamp_init (tRamp* const, float time, int samplesPerTick); | |
| 300 | + void tRamp_free (tRamp* const); | |
| 301 | + void tRamp_initToPool (tRamp* const, float time, int samplesPerTick, tMempool* const); | |
| 302 | + void tRamp_freeFromPool (tRamp* const, tMempool* const); | |
| 303 | + | |
| 304 | + float tRamp_tick (tRamp* const); | |
| 305 | + float tRamp_sample (tRamp* const); | |
| 306 | + void tRamp_setTime (tRamp* const, float time); | |
| 307 | + void tRamp_setDest (tRamp* const, float dest); | |
| 308 | + void tRamp_setVal (tRamp* const, float val); | |
| 309 | + | |
| 310 | + ///============= | |
| 311 | + /* Ramp Updown*/ | |
| 312 | + typedef struct _tRampUpDown { | |
| 313 | + float upInc; | |
| 314 | + float downInc; | |
| 315 | + float inv_sr_ms; | |
| 316 | + float minimum_time; | |
| 317 | + float curr,dest; | |
| 318 | + float upTime; | |
| 319 | + float downTime; | |
| 320 | + int samples_per_tick; | |
| 321 | + | |
| 322 | + } _tRampUpDown; | |
| 323 | + | |
| 324 | + typedef _tRampUpDown* tRampUpDown; | |
| 325 | + | |
| 326 | + void tRampUpDown_init (tRampUpDown* const, float upTime, float downTime, int samplesPerTick); | |
| 327 | + void tRampUpDown_free (tRampUpDown* const); | |
| 328 | + void tRampUpDown_initToPool (tRampUpDown* const, float upTime, float downTime, int samplesPerTick, tMempool* const); | |
| 329 | + void tRampUpDown_freeFromPool (tRampUpDown* const, tMempool* const); | |
| 330 | + | |
| 331 | + float tRampUpDown_tick (tRampUpDown* const); | |
| 332 | + float tRampUpDown_sample (tRampUpDown* const); | |
| 333 | + void tRampUpDown_setUpTime (tRampUpDown* const, float upTime); | |
| 334 | + void tRampUpDown_setDownTime (tRampUpDown* const, float downTime); | |
| 335 | + void tRampUpDown_setDest (tRampUpDown* const, float dest); | |
| 336 | + void tRampUpDown_setVal (tRampUpDown* const, float val); | |
| 337 | + | |
| 338 | + | |
| 339 | + ///========================= | |
| 340 | + //Slide (based on Max/MSP's slide~) | |
| 341 | + typedef struct _tSlide { | |
| 342 | + float prevOut; | |
| 343 | + float currentOut; | |
| 344 | + float prevIn; | |
| 345 | + float invUpSlide; | |
| 346 | + float invDownSlide; | |
| 347 | + | |
| 348 | + } _tSlide; | |
| 349 | + | |
| 350 | + typedef _tSlide* tSlide; | |
| 351 | + | |
| 352 | + void tSlide_init (tSlide* const, float upSlide, float downSlide); | |
| 353 | + void tSlide_free (tSlide* const); | |
| 354 | + void tSlide_initToPool (tSlide* const, float upSlide, float downSlide, tMempool* const); | |
| 355 | + void tSlide_freeFromPool (tSlide* const, tMempool* const); | |
| 356 | + | |
| 357 | + float tSlide_tick (tSlide* const, float in); | |
| 358 | + | |
| 359 | +#ifdef __cplusplus | |
| 360 | +} | |
| 361 | +#endif | |
| 362 | + | |
| 363 | +#endif // LEAF_ENVELOPES_H_INCLUDED | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| @@ -0,0 +1,486 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-filters.h | |
| 4 | + Created: 20 Jan 2017 12:01:10pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_FILTERS_H_INCLUDED | |
| 10 | +#define LEAF_FILTERS_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | + //============================================================================== | |
| 17 | + | |
| 18 | +#include "leaf-math.h" | |
| 19 | +#include "leaf-mempool.h" | |
| 20 | +#include "leaf-delay.h" | |
| 21 | +#include "leaf-tables.h" | |
| 22 | + | |
| 23 | + /*! | |
| 24 | + * @internal | |
| 25 | + * Header. | |
| 26 | + * @include basic-oscillators.h | |
| 27 | + * @example basic-oscillators.c | |
| 28 | + * An example. | |
| 29 | + */ | |
| 30 | + | |
| 31 | + //============================================================================== | |
| 32 | + | |
| 33 | + /* tAllpass: Schroeder allpass. Comb-filter with feedforward and feedback. */ | |
| 34 | + typedef struct _tAllpass | |
| 35 | + { | |
| 36 | + float gain; | |
| 37 | + | |
| 38 | + tLinearDelay delay; | |
| 39 | + | |
| 40 | + float lastOut; | |
| 41 | + | |
| 42 | + } _tAllpass; | |
| 43 | + | |
| 44 | + typedef _tAllpass* tAllpass; | |
| 45 | + | |
| 46 | + void tAllpass_init (tAllpass* const, float initDelay, uint32_t maxDelay); | |
| 47 | + void tAllpass_free (tAllpass* const); | |
| 48 | + void tAllpass_initToPool (tAllpass* const, float initDelay, uint32_t maxDelay, tMempool* const); | |
| 49 | + void tAllpass_freeFromPool (tAllpass* const, tMempool* const); | |
| 50 | + | |
| 51 | + float tAllpass_tick (tAllpass* const, float input); | |
| 52 | + void tAllpass_setGain (tAllpass* const, float gain); | |
| 53 | + void tAllpass_setDelay (tAllpass* const, float delay); | |
| 54 | + | |
| 55 | + | |
| 56 | + //============================================================================== | |
| 57 | + | |
| 58 | + /* tOnePole: OnePole filter, reimplemented from STK (Cook and Scavone). */ | |
| 59 | + typedef struct _tOnePole | |
| 60 | + { | |
| 61 | + float gain; | |
| 62 | + float a0,a1; | |
| 63 | + float b0,b1; | |
| 64 | + float lastIn, lastOut; | |
| 65 | + | |
| 66 | + | |
| 67 | + } _tOnePole; | |
| 68 | + | |
| 69 | + typedef _tOnePole* tOnePole; | |
| 70 | + | |
| 71 | + void tOnePole_init (tOnePole* const, float thePole); | |
| 72 | + void tOnePole_free (tOnePole* const); | |
| 73 | + void tOnePole_initToPool (tOnePole* const, float thePole, tMempool* const); | |
| 74 | + void tOnePole_freeFromPool (tOnePole* const, tMempool* const); | |
| 75 | + | |
| 76 | + float tOnePole_tick (tOnePole* const, float input); | |
| 77 | + void tOnePole_setB0 (tOnePole* const, float b0); | |
| 78 | + void tOnePole_setA1 (tOnePole* const, float a1); | |
| 79 | + void tOnePole_setPole (tOnePole* const, float thePole); | |
| 80 | + void tOnePole_setFreq (tOnePole* const, float freq); | |
| 81 | + void tOnePole_setCoefficients(tOnePole* const, float b0, float a1); | |
| 82 | + void tOnePole_setGain (tOnePole* const, float gain); | |
| 83 | + | |
| 84 | + //============================================================================== | |
| 85 | + | |
| 86 | + /* TwoPole filter, reimplemented from STK (Cook and Scavone). */ | |
| 87 | + typedef struct _tTwoPole | |
| 88 | + { | |
| 89 | + float gain; | |
| 90 | + float a0, a1, a2; | |
| 91 | + float b0; | |
| 92 | + | |
| 93 | + float radius, frequency; | |
| 94 | + oBool normalize; | |
| 95 | + | |
| 96 | + float lastOut[2]; | |
| 97 | + | |
| 98 | + } _tTwoPole; | |
| 99 | + | |
| 100 | + typedef _tTwoPole* tTwoPole; | |
| 101 | + | |
| 102 | + void tTwoPole_init (tTwoPole* const); | |
| 103 | + void tTwoPole_free (tTwoPole* const); | |
| 104 | + void tTwoPole_initToPool (tTwoPole* const, tMempool* const); | |
| 105 | + void tTwoPole_freeFromPool (tTwoPole* const, tMempool* const); | |
| 106 | + | |
| 107 | + float tTwoPole_tick (tTwoPole* const, float input); | |
| 108 | + void tTwoPole_setB0 (tTwoPole* const, float b0); | |
| 109 | + void tTwoPole_setA1 (tTwoPole* const, float a1); | |
| 110 | + void tTwoPole_setA2 (tTwoPole* const, float a2); | |
| 111 | + void tTwoPole_setResonance (tTwoPole* const, float freq, float radius, oBool normalize); | |
| 112 | + void tTwoPole_setCoefficients(tTwoPole* const, float b0, float a1, float a2); | |
| 113 | + void tTwoPole_setGain (tTwoPole* const, float gain); | |
| 114 | + | |
| 115 | + //============================================================================== | |
| 116 | + | |
| 117 | + /* OneZero filter, reimplemented from STK (Cook and Scavone). */ | |
| 118 | + typedef struct _tOneZero | |
| 119 | + { | |
| 120 | + float gain; | |
| 121 | + float b0,b1; | |
| 122 | + float lastIn, lastOut, frequency; | |
| 123 | + | |
| 124 | + } _tOneZero; | |
| 125 | + | |
| 126 | + typedef _tOneZero* tOneZero; | |
| 127 | + | |
| 128 | + void tOneZero_init (tOneZero* const, float theZero); | |
| 129 | + void tOneZero_free (tOneZero* const); | |
| 130 | + void tOneZero_initToPool (tOneZero* const, float theZero, tMempool* const); | |
| 131 | + void tOneZero_freeFromPool (tOneZero* const, tMempool* const); | |
| 132 | + | |
| 133 | + float tOneZero_tick (tOneZero* const, float input); | |
| 134 | + void tOneZero_setB0 (tOneZero* const, float b0); | |
| 135 | + void tOneZero_setB1 (tOneZero* const, float b1); | |
| 136 | + void tOneZero_setZero (tOneZero* const, float theZero); | |
| 137 | + void tOneZero_setCoefficients(tOneZero* const, float b0, float b1); | |
| 138 | + void tOneZero_setGain (tOneZero* const, float gain); | |
| 139 | + float tOneZero_getPhaseDelay (tOneZero *f, float frequency ); | |
| 140 | + | |
| 141 | + //============================================================================== | |
| 142 | + | |
| 143 | + /* TwoZero filter, reimplemented from STK (Cook and Scavone). */ | |
| 144 | + typedef struct _tTwoZero | |
| 145 | + { | |
| 146 | + float gain; | |
| 147 | + float b0, b1, b2; | |
| 148 | + | |
| 149 | + float frequency, radius; | |
| 150 | + | |
| 151 | + float lastIn[2]; | |
| 152 | + | |
| 153 | + } _tTwoZero; | |
| 154 | + | |
| 155 | + typedef _tTwoZero* tTwoZero; | |
| 156 | + | |
| 157 | + void tTwoZero_init (tTwoZero* const); | |
| 158 | + void tTwoZero_free (tTwoZero* const); | |
| 159 | + void tTwoZero_initToPool (tTwoZero* const, tMempool* const); | |
| 160 | + void tTwoZero_freeFromPool (tTwoZero* const, tMempool* const); | |
| 161 | + | |
| 162 | + float tTwoZero_tick (tTwoZero* const, float input); | |
| 163 | + void tTwoZero_setB0 (tTwoZero* const, float b0); | |
| 164 | + void tTwoZero_setB1 (tTwoZero* const, float b1); | |
| 165 | + void tTwoZero_setB2 (tTwoZero* const, float b2); | |
| 166 | + void tTwoZero_setNotch (tTwoZero* const, float frequency, float radius); | |
| 167 | + void tTwoZero_setCoefficients(tTwoZero* const, float b0, float b1, float b2); | |
| 168 | + void tTwoZero_setGain (tTwoZero* const, float gain); | |
| 169 | + | |
| 170 | + //============================================================================== | |
| 171 | + | |
| 172 | + /* PoleZero filter, reimplemented from STK (Cook and Scavone). */ | |
| 173 | + typedef struct _tPoleZero | |
| 174 | + { | |
| 175 | + float gain; | |
| 176 | + float a0,a1; | |
| 177 | + float b0,b1; | |
| 178 | + | |
| 179 | + float lastIn, lastOut; | |
| 180 | + | |
| 181 | + } _tPoleZero; | |
| 182 | + | |
| 183 | + typedef _tPoleZero* tPoleZero; | |
| 184 | + | |
| 185 | + void tPoleZero_init (tPoleZero* const); | |
| 186 | + void tPoleZero_free (tPoleZero* const); | |
| 187 | + void tPoleZero_initToPool (tPoleZero* const, tMempool* const); | |
| 188 | + void tPoleZero_freeFromPool (tPoleZero* const, tMempool* const); | |
| 189 | + | |
| 190 | + float tPoleZero_tick (tPoleZero* const, float input); | |
| 191 | + void tPoleZero_setB0 (tPoleZero* const, float b0); | |
| 192 | + void tPoleZero_setB1 (tPoleZero* const, float b1); | |
| 193 | + void tPoleZero_setA1 (tPoleZero* const, float a1); | |
| 194 | + void tPoleZero_setCoefficients (tPoleZero* const, float b0, float b1, float a1); | |
| 195 | + void tPoleZero_setAllpass (tPoleZero* const, float coeff); | |
| 196 | + void tPoleZero_setBlockZero (tPoleZero* const, float thePole); | |
| 197 | + void tPoleZero_setGain (tPoleZero* const, float gain); | |
| 198 | + | |
| 199 | + //============================================================================== | |
| 200 | + | |
| 201 | + /* BiQuad filter, reimplemented from STK (Cook and Scavone). */ | |
| 202 | + typedef struct _tBiQuad | |
| 203 | + { | |
| 204 | + float gain; | |
| 205 | + float a0, a1, a2; | |
| 206 | + float b0, b1, b2; | |
| 207 | + | |
| 208 | + float lastIn[2]; | |
| 209 | + float lastOut[2]; | |
| 210 | + | |
| 211 | + float frequency, radius; | |
| 212 | + oBool normalize; | |
| 213 | + } _tBiQuad; | |
| 214 | + | |
| 215 | + typedef _tBiQuad* tBiQuad; | |
| 216 | + | |
| 217 | + void tBiQuad_init (tBiQuad* const); | |
| 218 | + void tBiQuad_free (tBiQuad* const); | |
| 219 | + void tBiQuad_initToPool (tBiQuad* const, tMempool* const); | |
| 220 | + void tBiQuad_freeFromPool (tBiQuad* const, tMempool* const); | |
| 221 | + | |
| 222 | + float tBiQuad_tick (tBiQuad* const, float input); | |
| 223 | + void tBiQuad_setB0 (tBiQuad* const, float b0); | |
| 224 | + void tBiQuad_setB1 (tBiQuad* const, float b1); | |
| 225 | + void tBiQuad_setB2 (tBiQuad* const, float b2); | |
| 226 | + void tBiQuad_setA1 (tBiQuad* const, float a1); | |
| 227 | + void tBiQuad_setA2 (tBiQuad* const, float a2); | |
| 228 | + void tBiQuad_setNotch (tBiQuad* const, float freq, float radius); | |
| 229 | + void tBiQuad_setResonance (tBiQuad* const, float freq, float radius, oBool normalize); | |
| 230 | + void tBiQuad_setCoefficients(tBiQuad* const, float b0, float b1, float b2, float a1, float a2); | |
| 231 | + void tBiQuad_setGain (tBiQuad* const, float gain); | |
| 232 | + | |
| 233 | + //============================================================================== | |
| 234 | + | |
| 235 | +/* State Variable Filter, algorithm from Andy Simper. */ | |
| 236 | + typedef enum SVFType | |
| 237 | + { | |
| 238 | + SVFTypeHighpass = 0, | |
| 239 | + SVFTypeLowpass, | |
| 240 | + SVFTypeBandpass, | |
| 241 | + SVFTypeNotch, | |
| 242 | + SVFTypePeak, | |
| 243 | + SVFTypeLowShelf, | |
| 244 | + SVFTypeHighShelf | |
| 245 | + } SVFType; | |
| 246 | + | |
| 247 | + typedef struct _tSVF | |
| 248 | + { | |
| 249 | + SVFType type; | |
| 250 | + float cutoff, Q; | |
| 251 | + float ic1eq,ic2eq; | |
| 252 | + float g,k,a1,a2,a3,cH,cB,cL,cBK; | |
| 253 | + | |
| 254 | + | |
| 255 | + } _tSVF; | |
| 256 | + | |
| 257 | + typedef _tSVF* tSVF; | |
| 258 | + | |
| 259 | + void tSVF_init (tSVF* const, SVFType type, float freq, float Q); | |
| 260 | + void tSVF_free (tSVF* const); | |
| 261 | + void tSVF_initToPool (tSVF* const, SVFType type, float freq, float Q, tMempool* const); | |
| 262 | + void tSVF_freeFromPool (tSVF* const, tMempool* const); | |
| 263 | + | |
| 264 | + float tSVF_tick (tSVF* const, float v0); | |
| 265 | + void tSVF_setFreq (tSVF* const, float freq); | |
| 266 | + void tSVF_setQ (tSVF* const, float Q); | |
| 267 | + void tSVF_setFreqAndQ (tSVF* const svff, float freq, float Q); | |
| 268 | + //============================================================================== | |
| 269 | + | |
| 270 | + /* Efficient State Variable Filter for 14-bit control input, [0, 4096). */ | |
| 271 | + typedef struct _tEfficientSVF | |
| 272 | + { | |
| 273 | + SVFType type; | |
| 274 | + float cutoff, Q; | |
| 275 | + float ic1eq,ic2eq; | |
| 276 | + float g,k,a1,a2,a3; | |
| 277 | + | |
| 278 | + } _tEfficientSVF; | |
| 279 | + | |
| 280 | + typedef _tEfficientSVF* tEfficientSVF; | |
| 281 | + | |
| 282 | + void tEfficientSVF_init (tEfficientSVF* const, SVFType type, uint16_t input, float Q); | |
| 283 | + void tEfficientSVF_free (tEfficientSVF* const); | |
| 284 | + void tEfficientSVF_initToPool (tEfficientSVF* const, SVFType type, uint16_t input, float Q, tMempool* const); | |
| 285 | + void tEfficientSVF_freeFromPool (tEfficientSVF* const, tMempool* const); | |
| 286 | + | |
| 287 | + float tEfficientSVF_tick (tEfficientSVF* const, float v0); | |
| 288 | + void tEfficientSVF_setFreq (tEfficientSVF* const, uint16_t controlFreq); | |
| 289 | + void tEfficientSVF_setQ (tEfficientSVF* const, float Q); | |
| 290 | + | |
| 291 | + //============================================================================== | |
| 292 | + | |
| 293 | + /* Simple Highpass filter. */ | |
| 294 | + typedef struct _tHighpass | |
| 295 | + { | |
| 296 | + float xs, ys, R; | |
| 297 | + float frequency; | |
| 298 | + | |
| 299 | + } _tHighpass; | |
| 300 | + | |
| 301 | + typedef _tHighpass* tHighpass; | |
| 302 | + | |
| 303 | + void tHighpass_init (tHighpass* const, float freq); | |
| 304 | + void tHighpass_free (tHighpass* const); | |
| 305 | + void tHighpass_initToPool (tHighpass* const, float freq, tMempool* const); | |
| 306 | + void tHighpass_freeFromPool (tHighpass* const, tMempool* const); | |
| 307 | + | |
| 308 | + float tHighpass_tick (tHighpass* const, float x); | |
| 309 | + void tHighpass_setFreq (tHighpass* const, float freq); | |
| 310 | + float tHighpass_getFreq (tHighpass* const); | |
| 311 | + | |
| 312 | + //============================================================================== | |
| 313 | + | |
| 314 | + // Butterworth Filter | |
| 315 | +#define NUM_SVF_BW 16 | |
| 316 | + typedef struct _tButterworth | |
| 317 | + { | |
| 318 | + float gain; | |
| 319 | + | |
| 320 | + int N; | |
| 321 | + | |
| 322 | + tSVF low[NUM_SVF_BW]; | |
| 323 | + tSVF high[NUM_SVF_BW]; | |
| 324 | + | |
| 325 | + float f1,f2; | |
| 326 | + | |
| 327 | + } _tButterworth; | |
| 328 | + | |
| 329 | + typedef _tButterworth* tButterworth; | |
| 330 | + | |
| 331 | + void tButterworth_init (tButterworth* const, int N, float f1, float f2); | |
| 332 | + void tButterworth_free (tButterworth* const); | |
| 333 | + void tButterworth_initToPool (tButterworth* const, int N, float f1, float f2, tMempool* const); | |
| 334 | + void tButterworth_freeFromPool (tButterworth* const, tMempool* const); | |
| 335 | + | |
| 336 | + float tButterworth_tick (tButterworth* const, float input); | |
| 337 | + void tButterworth_setF1 (tButterworth* const, float in); | |
| 338 | + void tButterworth_setF2 (tButterworth* const, float in); | |
| 339 | + void tButterworth_setFreqs (tButterworth* const, float f1, float f2); | |
| 340 | + | |
| 341 | + //============================================================================== | |
| 342 | + | |
| 343 | + typedef struct _tFIR | |
| 344 | + { | |
| 345 | + float* past; | |
| 346 | + float* coeff; | |
| 347 | + int numTaps; | |
| 348 | + } _tFIR; | |
| 349 | + | |
| 350 | + typedef _tFIR* tFIR; | |
| 351 | + | |
| 352 | + void tFIR_init (tFIR* const, float* coeffs, int numTaps); | |
| 353 | + void tFIR_free (tFIR* const); | |
| 354 | + void tFIR_initToPool (tFIR* const, float* coeffs, int numTaps, tMempool* const); | |
| 355 | + void tFIR_freeFromPool (tFIR* const, tMempool* const); | |
| 356 | + | |
| 357 | + float tFIR_tick (tFIR* const, float input); | |
| 358 | + | |
| 359 | + | |
| 360 | + //============================================================================== | |
| 361 | + | |
| 362 | + | |
| 363 | + typedef struct _tMedianFilter | |
| 364 | + { | |
| 365 | + float* val; | |
| 366 | + int* age; | |
| 367 | + int m; | |
| 368 | + int size; | |
| 369 | + int middlePosition; | |
| 370 | + int last; | |
| 371 | + int pos; | |
| 372 | + } _tMedianFilter; | |
| 373 | + | |
| 374 | + typedef _tMedianFilter* tMedianFilter; | |
| 375 | + | |
| 376 | + void tMedianFilter_init (tMedianFilter* const, int size); | |
| 377 | + void tMedianFilter_free (tMedianFilter* const); | |
| 378 | + void tMedianFilter_initToPool (tMedianFilter* const, int size, tMempool* const); | |
| 379 | + void tMedianFilter_freeFromPool (tMedianFilter* const, tMempool* const); | |
| 380 | + | |
| 381 | + float tMedianFilter_tick (tMedianFilter* const, float input); | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + //Vadim Zavalishin style from VA book (from implementation in RSlib posted to kvr forum) | |
| 388 | + | |
| 389 | + typedef enum VZFilterType | |
| 390 | + { | |
| 391 | + Highpass = 0, | |
| 392 | + Lowpass, | |
| 393 | + BandpassSkirt, | |
| 394 | + BandpassPeak, | |
| 395 | + BandReject, | |
| 396 | + Bell, | |
| 397 | + Lowshelf, | |
| 398 | + Highshelf, | |
| 399 | + Morph, | |
| 400 | + Bypass, | |
| 401 | + Allpass | |
| 402 | + } VZFilterType; | |
| 403 | + | |
| 404 | + | |
| 405 | + typedef struct _tVZFilter | |
| 406 | + { | |
| 407 | + VZFilterType type; | |
| 408 | + // state: | |
| 409 | + float s1, s2; | |
| 410 | + | |
| 411 | + // filter coefficients: | |
| 412 | + float g; // embedded integrator gain | |
| 413 | + float R2; // twice the damping coefficient (R2 == 2*R == 1/Q) | |
| 414 | + float h; // factor for feedback (== 1/(1+2*R*g+g*g)) | |
| 415 | + float cL, cB, cH; // coefficients for low-, band-, and highpass signals | |
| 416 | + | |
| 417 | + // parameters: | |
| 418 | + float fs; // sample-rate | |
| 419 | + float fc; // characteristic frequency | |
| 420 | + float G; // gain | |
| 421 | + float invG; //1/gain | |
| 422 | + float B; // bandwidth (in octaves) | |
| 423 | + float m; // morph parameter (0...1) | |
| 424 | + | |
| 425 | + float sr; //local sampling rate of filter (may be different from leaf sr if oversampled) | |
| 426 | + float inv_sr; | |
| 427 | + | |
| 428 | + } _tVZFilter; | |
| 429 | + | |
| 430 | + typedef _tVZFilter* tVZFilter; | |
| 431 | + | |
| 432 | + void tVZFilter_init (tVZFilter* const, VZFilterType type, float freq, float Q); | |
| 433 | + void tVZFilter_free (tVZFilter* const); | |
| 434 | + void tVZFilter_initToPool (tVZFilter* const, VZFilterType type, float freq, float Q, tMempool* const); | |
| 435 | + void tVZFilter_freeFromPool (tVZFilter* const, tMempool* const); | |
| 436 | + void tVZFilter_setSampleRate (tVZFilter* const, float sampleRate); | |
| 437 | + float tVZFilter_tick (tVZFilter* const, float input); | |
| 438 | + float tVZFilter_tickEfficient (tVZFilter* const vf, float in); | |
| 439 | + void tVZFilter_calcCoeffs (tVZFilter* const); | |
| 440 | + void tVZFilter_setBandwidth (tVZFilter* const, float bandWidth); | |
| 441 | + void tVZFilter_setFreq (tVZFilter* const, float freq); | |
| 442 | + void tVZFilter_setFreqAndBandwidth (tVZFilter* const vf, float freq, float bw); | |
| 443 | + void tVZFilter_setGain (tVZFilter* const, float gain); | |
| 444 | + void tVZFilter_setType (tVZFilter* const, VZFilterType type); | |
| 445 | + float tVZFilter_BandwidthToR (tVZFilter* const vf, float B); | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + //diode ladder filter | |
| 451 | + typedef struct _tDiodeFilter | |
| 452 | + { | |
| 453 | + | |
| 454 | + float f; | |
| 455 | + float r; | |
| 456 | + float Vt; | |
| 457 | + float n; | |
| 458 | + float gamma; | |
| 459 | + float zi; | |
| 460 | + float g0inv; | |
| 461 | + float g1inv; | |
| 462 | + float g2inv; | |
| 463 | + float s0, s1, s2, s3; | |
| 464 | + | |
| 465 | + } _tDiodeFilter; | |
| 466 | + | |
| 467 | + typedef _tDiodeFilter* tDiodeFilter; | |
| 468 | + | |
| 469 | + void tDiodeFilter_init (tDiodeFilter* const, float freq, float Q); | |
| 470 | + void tDiodeFilter_free (tDiodeFilter* const); | |
| 471 | + void tDiodeFilter_initToPool (tDiodeFilter* const, float freq, float Q, tMempool* const); | |
| 472 | + void tDiodeFilter_freeFromPool (tDiodeFilter* const, tMempool* const); | |
| 473 | + | |
| 474 | + float tDiodeFilter_tick (tDiodeFilter* const, float input); | |
| 475 | + void tDiodeFilter_setFreq (tDiodeFilter* const vf, float cutoff); | |
| 476 | + void tDiodeFilter_setQ (tDiodeFilter* const vf, float resonance); | |
| 477 | + | |
| 478 | + | |
| 479 | +#ifdef __cplusplus | |
| 480 | +} | |
| 481 | +#endif | |
| 482 | + | |
| 483 | +#endif // LEAF_FILTERS_H_INCLUDED | |
| 484 | + | |
| 485 | +//============================================================================== | |
| 486 | + | |
| @@ -0,0 +1,52 @@ | ||
| 1 | +/* | |
| 2 | + ============================================================================== | |
| 3 | + | |
| 4 | + leaf-global.h | |
| 5 | + Created: 24 Oct 2019 2:24:38pm | |
| 6 | + Author: Matthew Wang | |
| 7 | + | |
| 8 | + ============================================================================== | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#ifndef LEAF_GLOBAL_H_INCLUDED | |
| 12 | +#define LEAF_GLOBAL_H_INCLUDED | |
| 13 | + | |
| 14 | +#ifdef __cplusplus | |
| 15 | +extern "C" { | |
| 16 | +#endif | |
| 17 | + | |
| 18 | +#include "leaf-mempool.h" | |
| 19 | + | |
| 20 | + /*! | |
| 21 | + * @ingroup leaf | |
| 22 | + * @brief The struct of the global LEAF instance `leaf`. Contains global variables and settings. | |
| 23 | + */ | |
| 24 | + struct LEAF | |
| 25 | + { | |
| 26 | + ///@{ | |
| 27 | + float sampleRate; //!< The current audio sample rate. Set with LEAF_setSampleRate(). | |
| 28 | + float invSampleRate; //!< The inverse of the current sample rate. | |
| 29 | + int blockSize; //!< The audio block size. | |
| 30 | + float twoPiTimesInvSampleRate; //!< Two-pi times the inverse of the current sample rate. | |
| 31 | + float (*random)(void); //!< A pointer to the random() function provided on initialization. | |
| 32 | + int clearOnAllocation; //!< A flag that determines whether memory allocated from the LEAF memory pool will be cleared. | |
| 33 | + tMempool mempool; //!< The default LEAF mempool object. | |
| 34 | + _tMempool _mempool; | |
| 35 | + size_t header_size; //!< The size in bytes of memory region headers within mempools. | |
| 36 | + ///@} | |
| 37 | + }; | |
| 38 | + typedef struct LEAF LEAF; | |
| 39 | + | |
| 40 | + extern LEAF leaf; | |
| 41 | + | |
| 42 | + //============================================================================== | |
| 43 | + | |
| 44 | +#ifdef __cplusplus | |
| 45 | +} | |
| 46 | +#endif | |
| 47 | + | |
| 48 | +#endif // LEAF_GLOBAL_H_INCLUDED | |
| 49 | + | |
| 50 | +//============================================================================== | |
| 51 | + | |
| 52 | + | |
| @@ -0,0 +1,209 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-instruments.h | |
| 4 | + Created: 30 Nov 2018 10:24:44am | |
| 5 | + Author: airship | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_INSTRUMENTS_H_INCLUDED | |
| 10 | +#define LEAF_INSTRUMENTS_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | + //============================================================================== | |
| 17 | + | |
| 18 | +#include "leaf-math.h" | |
| 19 | +#include "leaf-mempool.h" | |
| 20 | +#include "leaf-oscillators.h" | |
| 21 | +#include "leaf-filters.h" | |
| 22 | +#include "leaf-envelopes.h" | |
| 23 | + | |
| 24 | + /*! | |
| 25 | + * @internal | |
| 26 | + * Header. | |
| 27 | + * @include basic-oscillators.h | |
| 28 | + * @example basic-oscillators.c | |
| 29 | + * An example. | |
| 30 | + */ | |
| 31 | + | |
| 32 | + //============================================================================== | |
| 33 | + | |
| 34 | + // 808 Cowbell | |
| 35 | + typedef struct _t808Cowbell { | |
| 36 | + | |
| 37 | + tSquare p[2]; | |
| 38 | + tNoise stick; | |
| 39 | + tSVF bandpassOsc; | |
| 40 | + tSVF bandpassStick; | |
| 41 | + tEnvelope envGain; | |
| 42 | + tEnvelope envStick; | |
| 43 | + tEnvelope envFilter; | |
| 44 | + tHighpass highpass; | |
| 45 | + float oscMix; | |
| 46 | + float filterCutoff; | |
| 47 | + uint8_t useStick; | |
| 48 | + | |
| 49 | + } _t808Cowbell; | |
| 50 | + | |
| 51 | + typedef _t808Cowbell* t808Cowbell; | |
| 52 | + | |
| 53 | + void t808Cowbell_init (t808Cowbell* const, int useStick); | |
| 54 | + void t808Cowbell_free (t808Cowbell* const); | |
| 55 | + void t808Cowbell_initToPool (t808Cowbell* const, int useStick, tMempool* const); | |
| 56 | + void t808Cowbell_freeFromPool (t808Cowbell* const, tMempool* const); | |
| 57 | + | |
| 58 | + float t808Cowbell_tick (t808Cowbell* const); | |
| 59 | + void t808Cowbell_on (t808Cowbell* const, float vel); | |
| 60 | + void t808Cowbell_setDecay (t808Cowbell* const, float decay); | |
| 61 | + void t808Cowbell_setHighpassFreq (t808Cowbell* const, float freq); | |
| 62 | + void t808Cowbell_setBandpassFreq (t808Cowbell* const, float freq); | |
| 63 | + void t808Cowbell_setFreq (t808Cowbell* const, float freq); | |
| 64 | + void t808Cowbell_setOscMix (t808Cowbell* const, float oscMix); | |
| 65 | + void t808Cowbell_setStick (t808Cowbell* const, int useStick); | |
| 66 | + | |
| 67 | + //============================================================================== | |
| 68 | + | |
| 69 | + // 808 Hihat | |
| 70 | + typedef struct _t808Hihat { | |
| 71 | + | |
| 72 | + // 6 Square waves | |
| 73 | + tSquare p[6]; | |
| 74 | + tNoise n; | |
| 75 | + tSVF bandpassOsc; | |
| 76 | + tSVF bandpassStick; | |
| 77 | + tEnvelope envGain; | |
| 78 | + tEnvelope envStick; | |
| 79 | + tEnvelope noiseFMGain; | |
| 80 | + tHighpass highpass; | |
| 81 | + tNoise stick; | |
| 82 | + | |
| 83 | + float freq; | |
| 84 | + float stretch; | |
| 85 | + float FM_amount; | |
| 86 | + float oscNoiseMix; | |
| 87 | + | |
| 88 | + } _t808Hihat; | |
| 89 | + | |
| 90 | + typedef _t808Hihat* t808Hihat; | |
| 91 | + | |
| 92 | + void t808Hihat_init (t808Hihat* const); | |
| 93 | + void t808Hihat_free (t808Hihat* const); | |
| 94 | + void t808Hihat_initToPool (t808Hihat* const, tMempool* const); | |
| 95 | + void t808Hihat_freeFromPool (t808Hihat* const, tMempool* const); | |
| 96 | + | |
| 97 | + float t808Hihat_tick (t808Hihat* const); | |
| 98 | + void t808Hihat_on (t808Hihat* const, float vel); | |
| 99 | + void t808Hihat_setOscNoiseMix (t808Hihat* const, float oscNoiseMix); | |
| 100 | + void t808Hihat_setDecay (t808Hihat* const, float decay); | |
| 101 | + void t808Hihat_setHighpassFreq (t808Hihat* const, float freq); | |
| 102 | + void t808Hihat_setOscBandpassFreq (t808Hihat* const, float freq); | |
| 103 | + void t808Hihat_setOscBandpassQ (t808Hihat* const hihat, float Q); | |
| 104 | + void t808Hihat_setStickBandPassFreq (t808Hihat* const, float freq); | |
| 105 | + void t808Hihat_setStickBandPassQ (t808Hihat* const hihat, float Q); | |
| 106 | + void t808Hihat_setOscFreq (t808Hihat* const, float freq); | |
| 107 | + void t808Hihat_setStretch (t808Hihat* const hihat, float stretch); | |
| 108 | + void t808Hihat_setFM (t808Hihat* const hihat, float FM_amount); | |
| 109 | + | |
| 110 | + //============================================================================== | |
| 111 | + | |
| 112 | + // 808 Snare | |
| 113 | + typedef struct _t808Snare { | |
| 114 | + | |
| 115 | + // Tone 1, Tone 2, Noise | |
| 116 | + tTriangle tone[2]; // Tri (not yet antialiased or wavetabled) | |
| 117 | + tNoise noiseOsc; | |
| 118 | + tSVF toneLowpass[2]; | |
| 119 | + tSVF noiseLowpass; // Lowpass from SVF filter | |
| 120 | + tEnvelope toneEnvOsc[2]; | |
| 121 | + tEnvelope toneEnvGain[2]; | |
| 122 | + tEnvelope noiseEnvGain; | |
| 123 | + tEnvelope toneEnvFilter[2]; | |
| 124 | + tEnvelope noiseEnvFilter; | |
| 125 | + | |
| 126 | + float toneGain[2]; | |
| 127 | + float noiseGain; | |
| 128 | + | |
| 129 | + float toneNoiseMix; | |
| 130 | + | |
| 131 | + float tone1Freq, tone2Freq; | |
| 132 | + | |
| 133 | + float noiseFilterFreq; | |
| 134 | + | |
| 135 | + } _t808Snare; | |
| 136 | + | |
| 137 | + typedef _t808Snare* t808Snare; | |
| 138 | + | |
| 139 | + void t808Snare_init (t808Snare* const); | |
| 140 | + void t808Snare_free (t808Snare* const); | |
| 141 | + void t808Snare_initToPool (t808Snare* const, tMempool* const); | |
| 142 | + void t808Snare_freeFromPool (t808Snare* const, tMempool* const); | |
| 143 | + | |
| 144 | + float t808Snare_tick (t808Snare* const); | |
| 145 | + void t808Snare_on (t808Snare* const, float vel); | |
| 146 | + void t808Snare_setTone1Freq (t808Snare* const, float freq); | |
| 147 | + void t808Snare_setTone2Freq (t808Snare* const, float freq); | |
| 148 | + void t808Snare_setTone1Decay (t808Snare* const, float decay); | |
| 149 | + void t808Snare_setTone2Decay (t808Snare* const, float decay); | |
| 150 | + void t808Snare_setNoiseDecay (t808Snare* const, float decay); | |
| 151 | + void t808Snare_setToneNoiseMix (t808Snare* const, float toneNoiseMix); | |
| 152 | + void t808Snare_setNoiseFilterFreq (t808Snare* const, float noiseFilterFreq); | |
| 153 | + void t808Snare_setNoiseFilterQ (t808Snare* const, float noiseFilterQ); | |
| 154 | + | |
| 155 | + //============================================================================== | |
| 156 | + | |
| 157 | + // 808 Kick | |
| 158 | + typedef struct _t808Kick { | |
| 159 | + | |
| 160 | + | |
| 161 | + tCycle tone; // Tri | |
| 162 | + tNoise noiseOsc; | |
| 163 | + tSVF toneLowpass; | |
| 164 | + tEnvelope toneEnvOscChirp; | |
| 165 | + tEnvelope toneEnvOscSigh; | |
| 166 | + tEnvelope toneEnvGain; | |
| 167 | + tEnvelope noiseEnvGain; | |
| 168 | + tEnvelope toneEnvFilter; | |
| 169 | + | |
| 170 | + float toneGain; | |
| 171 | + float noiseGain; | |
| 172 | + | |
| 173 | + float toneInitialFreq; | |
| 174 | + float sighAmountInHz; | |
| 175 | + float chirpRatioMinusOne; | |
| 176 | + float noiseFilterFreq; | |
| 177 | + | |
| 178 | + } _t808Kick; | |
| 179 | + | |
| 180 | + typedef _t808Kick* t808Kick; | |
| 181 | + | |
| 182 | + void t808Kick_init (t808Kick* const); | |
| 183 | + void t808Kick_free (t808Kick* const); | |
| 184 | + void t808Kick_initToPool (t808Kick* const, tMempool* const); | |
| 185 | + void t808Kick_freeFromPool (t808Kick* const, tMempool* const); | |
| 186 | + | |
| 187 | + float t808Kick_tick (t808Kick* const); | |
| 188 | + void t808Kick_on (t808Kick* const, float vel); | |
| 189 | + void t808Kick_setToneFreq (t808Kick* const, float freq); | |
| 190 | + void t808Kick_setToneDecay (t808Kick* const, float decay); | |
| 191 | + void t808Kick_setNoiseDecay (t808Kick* const, float decay); | |
| 192 | + void t808Kick_setSighAmount (t808Kick* const, float sigh); | |
| 193 | + void t808Kick_setChirpAmount (t808Kick* const, float chirp); | |
| 194 | + void t808Kick_setToneNoiseMix (t808Kick* const, float toneNoiseMix); | |
| 195 | + void t808Kick_setNoiseFilterFreq (t808Kick* const, float noiseFilterFreq); | |
| 196 | + void t808Kick_setNoiseFilterQ (t808Kick* const, float noiseFilterQ); | |
| 197 | + | |
| 198 | + //============================================================================== | |
| 199 | + | |
| 200 | +#ifdef __cplusplus | |
| 201 | +} | |
| 202 | +#endif | |
| 203 | + | |
| 204 | +#endif // LEAF_INSTRUMENTS_H_INCLUDED | |
| 205 | + | |
| 206 | +//============================================================================== | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| @@ -0,0 +1,212 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-math.h | |
| 4 | + Created: 22 Jan 2017 7:02:56pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_MATH_H_INCLUDED | |
| 10 | +#define LEAF_MATH_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | +#include "leaf-global.h" | |
| 17 | +#include "math.h" | |
| 18 | +#include "stdint.h" | |
| 19 | +#include "stdlib.h" | |
| 20 | + | |
| 21 | + //============================================================================== | |
| 22 | + | |
| 23 | + //============================================================================== | |
| 24 | + | |
| 25 | + typedef enum oBool | |
| 26 | + { | |
| 27 | + OTRUE = 1, | |
| 28 | + OFALSE = 0 | |
| 29 | + }oBool; | |
| 30 | + | |
| 31 | + // Allows for bitwise operations on floats | |
| 32 | + union unholy_t { /* a union between a float and an integer */ | |
| 33 | + float f; | |
| 34 | + int i; | |
| 35 | + }; | |
| 36 | + | |
| 37 | +#define SQRT8 2.82842712475f | |
| 38 | +#define WSCALE 1.30612244898f | |
| 39 | +#define PI (3.14159265358979f) | |
| 40 | +#define TWO_PI (6.28318530717958f) | |
| 41 | +#define HALF_PI (1.570796326794897f) | |
| 42 | + | |
| 43 | +#define VSF 1.0e-38f | |
| 44 | + | |
| 45 | +#define MAX_DELAY 8192 | |
| 46 | +#define INV_128 0.0078125f | |
| 47 | + | |
| 48 | +#define INV_20 0.05f | |
| 49 | +#define INV_40 0.025f | |
| 50 | +#define INV_80 0.0125f | |
| 51 | +#define INV_160 0.00625f | |
| 52 | +#define INV_320 0.003125f | |
| 53 | +#define INV_640 0.0015625f | |
| 54 | +#define INV_1280 0.00078125f | |
| 55 | +#define INV_2560 0.000390625f | |
| 56 | +#define INV_5120 0.0001953125f | |
| 57 | +#define INV_10240 0.00009765625f | |
| 58 | +#define INV_20480 0.000048828125f | |
| 59 | + | |
| 60 | + | |
| 61 | +#define INV_TWELVE 0.0833333333f | |
| 62 | +#define INV_440 0.0022727273f | |
| 63 | + | |
| 64 | +#define LOG2 0.3010299957f | |
| 65 | +#define INV_LOG2 3.321928095f | |
| 66 | + | |
| 67 | +#define SOS_M 343.0f | |
| 68 | +#define TWO_TO_5 32.0f | |
| 69 | +#define INV_TWO_TO_5 0.03125f | |
| 70 | +#define TWO_TO_7 128.f | |
| 71 | +#define INV_TWO_TO_7 0.0078125f | |
| 72 | +#define TWO_TO_8 256.f | |
| 73 | +#define INV_TWO_TO_8 0.00390625f | |
| 74 | +#define TWO_TO_9 512.f | |
| 75 | +#define INV_TWO_TO_9 0.001953125f | |
| 76 | +#define TWO_TO_10 1024.f | |
| 77 | +#define INV_TWO_TO_10 0.0009765625f | |
| 78 | +#define TWO_TO_11 2048.f | |
| 79 | +#define INV_TWO_TO_11 0.00048828125f | |
| 80 | +#define TWO_TO_12 4096.f | |
| 81 | +#define INV_TWO_TO_12 0.00024414062f | |
| 82 | +#define TWO_TO_15 32768.f | |
| 83 | +#define TWO_TO_16 65536.f | |
| 84 | +#define INV_TWO_TO_15 0.00003051757f | |
| 85 | +#define INV_TWO_TO_16 0.00001525878f | |
| 86 | +#define TWO_TO_16_MINUS_ONE 65535.0f | |
| 87 | +#define TWO_TO_23 8388608.0f | |
| 88 | +#define INV_TWO_TO_23 0.00000011920929f | |
| 89 | +#define TWO_TO_31 2147483648.0f | |
| 90 | +#define INV_TWO_TO_31 0.000000000465661f | |
| 91 | +#define TWO_TO_32 4294967296.0f | |
| 92 | +#define INV_TWO_TO_32 0.000000000232831f | |
| 93 | + | |
| 94 | +#define ONE_OVER_SQRT2 0.707106781186548f | |
| 95 | + | |
| 96 | +#define LOGTEN 2.302585092994 | |
| 97 | + | |
| 98 | + // Jones shaper | |
| 99 | + float LEAF_shaper (float input, float m_drive); | |
| 100 | + float LEAF_reedTable (float input, float offset, float slope); | |
| 101 | + | |
| 102 | + float LEAF_reduct (float input, float ratio); | |
| 103 | + float LEAF_round (float input, float rnd); | |
| 104 | + float LEAF_bitwise_xor(float input, uint32_t op); | |
| 105 | + | |
| 106 | + float LEAF_reduct (float input, float ratio); | |
| 107 | + float LEAF_round (float input, float rnd); | |
| 108 | + float LEAF_bitwise_xor(float input, uint32_t op); | |
| 109 | + | |
| 110 | + float LEAF_clip (float min, float val, float max); | |
| 111 | + int LEAF_clipInt (int min, int val, int max); | |
| 112 | + float LEAF_softClip (float val, float thresh); | |
| 113 | + oBool LEAF_isPrime (uint64_t number ); | |
| 114 | + | |
| 115 | + float LEAF_midiToFrequency (float f); | |
| 116 | + float LEAF_frequencyToMidi(float f); | |
| 117 | + | |
| 118 | + void LEAF_generate_sine (float* buffer, int size); | |
| 119 | + void LEAF_generate_sawtooth (float* buffer, float basefreq, int size); | |
| 120 | + void LEAF_generate_triangle (float* buffer, float basefreq, int size); | |
| 121 | + void LEAF_generate_square (float* buffer, float basefreq, int size); | |
| 122 | + | |
| 123 | + // dope af | |
| 124 | + float LEAF_chebyshevT(float in, int n); | |
| 125 | + float LEAF_CompoundChebyshevT(float in, int n, float* amps); | |
| 126 | + | |
| 127 | + // Hermite interpolation | |
| 128 | + float LEAF_interpolate_hermite (float A, float B, float C, float D, float t); | |
| 129 | + float LEAF_interpolate_hermite_x(float yy0, float yy1, float yy2, float yy3, float xx); | |
| 130 | + float LEAF_interpolation_linear (float A, float B, float t); | |
| 131 | + | |
| 132 | + float interpolate3max(float *buf, const int peakindex); | |
| 133 | + float interpolate3phase(float *buf, const int peakindex); | |
| 134 | + | |
| 135 | + // alternative implementation for abs() | |
| 136 | + // REQUIRES: 32 bit integers | |
| 137 | + int fastabs_int(int in); | |
| 138 | + | |
| 139 | + // alternative implementation for abs() | |
| 140 | + // REQUIRES: 32 bit floats | |
| 141 | + float fastabsf(float f); | |
| 142 | + | |
| 143 | + float fastexp2f(float f); | |
| 144 | + | |
| 145 | + float fastPowf(float a, float b) ; | |
| 146 | + double fastPow(double a, double b); | |
| 147 | + | |
| 148 | + | |
| 149 | + void LEAF_crossfade(float fade, float* volumes); | |
| 150 | + | |
| 151 | + | |
| 152 | + float LEAF_tanh(float x); | |
| 153 | + void LEAF_generate_sine(float* buffer, int size); | |
| 154 | + void LEAF_generate_sawtooth(float* buffer, float basefreq, int size); | |
| 155 | + void LEAF_generate_triangle(float* buffer, float basefreq, int size); | |
| 156 | + void LEAF_generate_square(float* buffer, float basefreq, int size); | |
| 157 | + | |
| 158 | + //0.001 base gives a good curve that goes from 1 to near zero | |
| 159 | + //1000 gives a good curve from -1.0 to 0.0 | |
| 160 | + void LEAF_generate_exp(float* buffer, float base, float start, float end, float offset, int size); | |
| 161 | + | |
| 162 | + float LEAF_poly_blep(float t, float dt); | |
| 163 | + float LEAF_midiToFrequency(float f); | |
| 164 | + | |
| 165 | + | |
| 166 | + float fast_mtof(float f); | |
| 167 | + | |
| 168 | + double fastexp(double x); | |
| 169 | + | |
| 170 | + float fastexpf(float x); | |
| 171 | + | |
| 172 | + double fasterexp(double x); | |
| 173 | + | |
| 174 | + float fasterexpf(float x); | |
| 175 | + | |
| 176 | + float mtof(float f); | |
| 177 | + | |
| 178 | + float fast_mtof(float f); | |
| 179 | + | |
| 180 | + float faster_mtof(float f); | |
| 181 | + | |
| 182 | + float ftom(float f); | |
| 183 | + | |
| 184 | + float powtodb(float f); | |
| 185 | + | |
| 186 | + float rmstodb(float f); | |
| 187 | + | |
| 188 | + float dbtopow(float f); | |
| 189 | + | |
| 190 | + float dbtorms(float f); | |
| 191 | + | |
| 192 | + float atodb(float a); | |
| 193 | + | |
| 194 | + float dbtoa(float db); | |
| 195 | + | |
| 196 | + float fastdbtoa(float db); | |
| 197 | + | |
| 198 | + float maximum (float num1, float num2); | |
| 199 | + | |
| 200 | + float minimum (float num1, float num2); | |
| 201 | + | |
| 202 | + | |
| 203 | + //============================================================================== | |
| 204 | + | |
| 205 | +#ifdef __cplusplus | |
| 206 | +} | |
| 207 | +#endif | |
| 208 | + | |
| 209 | +#endif // LEAF_MATH_H_INCLUDED | |
| 210 | + | |
| 211 | +//============================================================================== | |
| 212 | + | |
| @@ -0,0 +1,159 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + In short, mpool is distributed under so called "BSD license", | |
| 4 | + | |
| 5 | + Copyright (c) 2009-2010 Tatsuhiko Kubo <cubicdaiya@gmail.com> | |
| 6 | + All rights reserved. | |
| 7 | + | |
| 8 | + Redistribution and use in source and binary forms, with or without modification, | |
| 9 | + are permitted provided that the following conditions are met: | |
| 10 | + | |
| 11 | + * Redistributions of source code must retain the above copyright notice, | |
| 12 | + this list of conditions and the following disclaimer. | |
| 13 | + | |
| 14 | + * Redistributions in binary form must reproduce the above copyright notice, | |
| 15 | + this list of conditions and the following disclaimer in the documentation | |
| 16 | + and/or other materials provided with the distribution. | |
| 17 | + | |
| 18 | + * Neither the name of the authors nor the names of its contributors | |
| 19 | + may be used to endorse or promote products derived from this software | |
| 20 | + without specific prior written permission. | |
| 21 | + | |
| 22 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 23 | + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 24 | + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 25 | + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 26 | + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 27 | + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |
| 28 | + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 29 | + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
| 30 | + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 31 | + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 32 | + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 33 | + | |
| 34 | + written by C99 style | |
| 35 | + ==============================================================================*/ | |
| 36 | + | |
| 37 | +#ifndef LEAF_MPOOL_H_INCLUDED | |
| 38 | +#define LEAF_MPOOL_H_INCLUDED | |
| 39 | + | |
| 40 | +#ifdef __cplusplus | |
| 41 | +extern "C" { | |
| 42 | +#endif | |
| 43 | + | |
| 44 | + //============================================================================== | |
| 45 | + | |
| 46 | +#include <stdio.h> | |
| 47 | +#include <stdlib.h> | |
| 48 | +#include <string.h> | |
| 49 | +#include <stdbool.h> | |
| 50 | + | |
| 51 | + //============================================================================== | |
| 52 | + | |
| 53 | +#define MPOOL_ALIGN_SIZE (8) | |
| 54 | + | |
| 55 | + //#define size_t unsigned long | |
| 56 | + | |
| 57 | + /*! | |
| 58 | + * @defgroup tmempool tMempool | |
| 59 | + * @ingroup mempool | |
| 60 | + * An object that can contain an additional mempool for the allocation of LEAF objects. | |
| 61 | + * @{ | |
| 62 | + */ | |
| 63 | + | |
| 64 | + // node of free list | |
| 65 | + typedef struct mpool_node_t { | |
| 66 | + char *pool; // memory pool field | |
| 67 | + struct mpool_node_t *next; // next node pointer | |
| 68 | + struct mpool_node_t *prev; // prev node pointer | |
| 69 | + size_t size; | |
| 70 | + } mpool_node_t; | |
| 71 | + | |
| 72 | + typedef struct _tMempool | |
| 73 | + { | |
| 74 | + char* mpool; // start of the mpool | |
| 75 | + size_t usize; // used size of the pool | |
| 76 | + size_t msize; // max size of the pool | |
| 77 | + mpool_node_t* head; // first node of memory pool free list | |
| 78 | + } _tMempool; | |
| 79 | + | |
| 80 | + typedef _tMempool* tMempool; | |
| 81 | + | |
| 82 | + //! Initialize a tMempool for a given memory location and size to the default LEAF mempool. | |
| 83 | + /*! | |
| 84 | + @param pool A pointer to the tMempool to be initialized. | |
| 85 | + @param memory A pointer to the chunk of memory to be used as a mempool. | |
| 86 | + @param size The size of the chunk of memory to be used as a mempool. | |
| 87 | + */ | |
| 88 | + void tMempool_init (tMempool* const pool, char* memory, size_t size); | |
| 89 | + | |
| 90 | + | |
| 91 | + //! Free a tMempool from the default LEAF mempool. | |
| 92 | + /*! | |
| 93 | + @param pool A pointer to the tMempool to be freed. | |
| 94 | + */ | |
| 95 | + void tMempool_free (tMempool* const pool); | |
| 96 | + | |
| 97 | + | |
| 98 | + //! Initialize a tMempool for a given memory location and size to a specified mempool. | |
| 99 | + /*! | |
| 100 | + @param pool A pointer to the tMempool to be initialized. | |
| 101 | + @param memory A pointer to the chunk of memory to be used as a mempool. | |
| 102 | + @param size The size of the chuck of memory to be used as a mempool. | |
| 103 | + @param poolTo A pointer to the tMempool to which a tMempool should be initialized. | |
| 104 | + */ | |
| 105 | + void tMempool_initToPool (tMempool* const pool, char* memory, size_t size, tMempool* const poolTo); | |
| 106 | + | |
| 107 | + | |
| 108 | + //! Free a tMempool from a specified mempool. | |
| 109 | + /*! | |
| 110 | + @param pool A pointer to the tMempool to be freed from the default LEAF mempool. | |
| 111 | + @param poolFrom A pointer to the tMempool from which a tMempool should be freed. | |
| 112 | + */ | |
| 113 | + void tMempool_freeFromPool (tMempool* const pool, tMempool* const poolFrom); | |
| 114 | + | |
| 115 | + /*! @} */ | |
| 116 | + | |
| 117 | + //============================================================================== | |
| 118 | + | |
| 119 | + // typedef struct mpool_t { | |
| 120 | + // char* mpool; // start of the mpool | |
| 121 | + // size_t usize; // used size of the pool | |
| 122 | + // size_t msize; // max size of the pool | |
| 123 | + // mpool_node_t* head; // first node of memory pool free list | |
| 124 | + // } mpool_t; | |
| 125 | + | |
| 126 | + void mpool_create (char* memory, size_t size, _tMempool* pool); | |
| 127 | + | |
| 128 | + char* mpool_alloc(size_t size, _tMempool* pool); | |
| 129 | + char* mpool_calloc(size_t asize, _tMempool* pool); | |
| 130 | + | |
| 131 | + void mpool_free(char* ptr, _tMempool* pool); | |
| 132 | + | |
| 133 | + size_t mpool_get_size(_tMempool* pool); | |
| 134 | + size_t mpool_get_used(_tMempool* pool); | |
| 135 | + | |
| 136 | + void leaf_pool_init(char* memory, size_t size); | |
| 137 | + | |
| 138 | + char* leaf_alloc(size_t size); | |
| 139 | + char* leaf_calloc(size_t size); | |
| 140 | + | |
| 141 | + void leaf_free(char* ptr); | |
| 142 | + | |
| 143 | + size_t leaf_pool_get_size(void); | |
| 144 | + size_t leaf_pool_get_used(void); | |
| 145 | + | |
| 146 | + char* leaf_pool_get_pool(void); | |
| 147 | + | |
| 148 | + void leaf_mempool_overrun(void); | |
| 149 | + | |
| 150 | +#ifdef __cplusplus | |
| 151 | +} | |
| 152 | +#endif | |
| 153 | + | |
| 154 | +#endif // LEAF_MPOOL_H | |
| 155 | + | |
| 156 | +//============================================================================== | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| @@ -0,0 +1,510 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-midi.h | |
| 4 | + Created: 30 Nov 2018 11:29:26am | |
| 5 | + Author: airship | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_MIDI_H_INCLUDED | |
| 10 | +#define LEAF_MIDI_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | + //============================================================================== | |
| 17 | + | |
| 18 | +#include "leaf-global.h" | |
| 19 | +#include "leaf-mempool.h" | |
| 20 | +#include "leaf-math.h" | |
| 21 | +#include "leaf-envelopes.h" | |
| 22 | + | |
| 23 | + /*! | |
| 24 | + * @internal | |
| 25 | + * Header. | |
| 26 | + * @include basic-oscillators.h | |
| 27 | + * @example basic-oscillators.c | |
| 28 | + * An example. | |
| 29 | + */ | |
| 30 | + | |
| 31 | + //============================================================================== | |
| 32 | + | |
| 33 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 34 | + | |
| 35 | + /*! | |
| 36 | + * @defgroup tstack tStack | |
| 37 | + * @ingroup midi | |
| 38 | + * @brief A basic stack of integers with a fixed max size of 128, used by tPoly to keep track of MIDI notes. | |
| 39 | + * @{ | |
| 40 | + */ | |
| 41 | + | |
| 42 | + // STACK implementation (fixed size) | |
| 43 | +#define STACK_SIZE 128 | |
| 44 | + typedef struct _tStack | |
| 45 | + { | |
| 46 | + int data[STACK_SIZE]; | |
| 47 | + uint16_t pos; | |
| 48 | + uint16_t size; | |
| 49 | + uint16_t capacity; | |
| 50 | + oBool ordered; | |
| 51 | + | |
| 52 | + } _tStack; | |
| 53 | + | |
| 54 | + typedef _tStack* tStack; | |
| 55 | + | |
| 56 | + //! Initialize a tStack to the default LEAF mempool. | |
| 57 | + /*! | |
| 58 | + @param stack A pointer to the tStack to be initialized. | |
| 59 | + */ | |
| 60 | + void tStack_init (tStack* const stack); | |
| 61 | + | |
| 62 | + | |
| 63 | + //! Free a tStack from the default LEAF mempool. | |
| 64 | + /*! | |
| 65 | + @param stack A pointer to the tStack to be freed. | |
| 66 | + */ | |
| 67 | + void tStack_free (tStack* const stack); | |
| 68 | + | |
| 69 | + | |
| 70 | + //! Initialize a tStack to a specified mempool. | |
| 71 | + /*! | |
| 72 | + @param stack A pointer to the tStack to be initialized. | |
| 73 | + @param pool A pointer to the tMempool to which the tStack should be initialized. | |
| 74 | + */ | |
| 75 | + void tStack_initToPool (tStack* const stack, tMempool* const pool); | |
| 76 | + | |
| 77 | + | |
| 78 | + //! Free a tStack from a specified mempool. | |
| 79 | + /*! | |
| 80 | + @param stack A pointer to the tStack to be freed. | |
| 81 | + @param pool A pointer to the tMempool from which the tStack should be freed. | |
| 82 | + */ | |
| 83 | + void tStack_freeFromPool (tStack* const stack, tMempool* const pool); | |
| 84 | + | |
| 85 | + //! Set the capacity of the stack. | |
| 86 | + /*! | |
| 87 | + @param stack A pointer to the relevant tStack. | |
| 88 | + @param cap The new capacity. | |
| 89 | + */ | |
| 90 | + void tStack_setCapacity (tStack* const stack, uint16_t cap); | |
| 91 | + | |
| 92 | + | |
| 93 | + //! Add a value to the stack only if that value is not already in the stack. | |
| 94 | + /*! | |
| 95 | + @param stack A pointer to the relevant tStack. | |
| 96 | + @param item The value to be added. | |
| 97 | + */ | |
| 98 | + int tStack_addIfNotAlreadyThere (tStack* const stack, uint16_t item); | |
| 99 | + | |
| 100 | + | |
| 101 | + //! Add a value to the stack. | |
| 102 | + /*! | |
| 103 | + @param stack A pointer to the relevant tStack. | |
| 104 | + @param item The value to be added. | |
| 105 | + */ | |
| 106 | + void tStack_add (tStack* const stack, uint16_t item); | |
| 107 | + | |
| 108 | + | |
| 109 | + //! Remove a single instance of a value from the stack. | |
| 110 | + /*! | |
| 111 | + @param stack A pointer to the relevant tStack. | |
| 112 | + @param item The value to be removed. | |
| 113 | + */ | |
| 114 | + int tStack_remove (tStack* const stack, uint16_t item); | |
| 115 | + | |
| 116 | + | |
| 117 | + //! Clear the stack. | |
| 118 | + /*! | |
| 119 | + @param stack A pointer to the relevant tStack. | |
| 120 | + */ | |
| 121 | + void tStack_clear (tStack* const stack); | |
| 122 | + | |
| 123 | + | |
| 124 | + //! Get the first value in the stack. | |
| 125 | + /*! | |
| 126 | + @param stack A pointer to the relevant tStack. | |
| 127 | + @return The first value in the stack. | |
| 128 | + */ | |
| 129 | + int tStack_first (tStack* const stack); | |
| 130 | + | |
| 131 | + | |
| 132 | + //! Get the current size of the stack. | |
| 133 | + /*! | |
| 134 | + @param stack A pointer to the relevant tStack. | |
| 135 | + @return The current size of the stack. | |
| 136 | + */ | |
| 137 | + int tStack_getSize (tStack* const stack); | |
| 138 | + | |
| 139 | + //! Check if the stack contains a value, and if it does, get the index of that value. | |
| 140 | + /*! | |
| 141 | + @param stack A pointer to the relevant tStack. | |
| 142 | + @param item The value to check against the stack. | |
| 143 | + @return The index of the value or -1 if the stack does not contain the value. | |
| 144 | + */ | |
| 145 | + int tStack_contains (tStack* const stack, uint16_t item); | |
| 146 | + | |
| 147 | + //! Get the next value in the stack, starting from the earliest added values. | |
| 148 | + /*! | |
| 149 | + @param stack A pointer to the relevant tStack. | |
| 150 | + @return The next value in the stack or -1 if there are no values in the stack. | |
| 151 | + */ | |
| 152 | + int tStack_next (tStack* const stack); | |
| 153 | + | |
| 154 | + //! Get the value at a given index of the stack. | |
| 155 | + /*! | |
| 156 | + @param stack A pointer to the relevant tStack. | |
| 157 | + @param index The index of the stack from which to get a value. | |
| 158 | + @return The value at the given index. | |
| 159 | + */ | |
| 160 | + int tStack_get (tStack* const stack, int index); | |
| 161 | + | |
| 162 | + /*! @} */ | |
| 163 | + | |
| 164 | + /*! | |
| 165 | + * @defgroup tpoly tPoly | |
| 166 | + * @ingroup midi | |
| 167 | + * @brief An object for polyphonic handling. | |
| 168 | + * @{ | |
| 169 | + */ | |
| 170 | + | |
| 171 | + /* tPoly */ | |
| 172 | + typedef struct _tPoly | |
| 173 | + { | |
| 174 | + tStack stack; | |
| 175 | + tStack orderStack; | |
| 176 | + | |
| 177 | + tRamp* ramps; | |
| 178 | + float* rampVals; | |
| 179 | + oBool* firstReceived; | |
| 180 | + float glideTime; | |
| 181 | + oBool pitchGlideIsActive; | |
| 182 | + | |
| 183 | + int numVoices; | |
| 184 | + int maxNumVoices; | |
| 185 | + | |
| 186 | + //int voices[POLY_NUM_MAX_VOICES][2]; | |
| 187 | + int** voices; | |
| 188 | + | |
| 189 | + int notes[128][2]; | |
| 190 | + | |
| 191 | + int CCs[128]; | |
| 192 | + | |
| 193 | + uint8_t CCsRaw[128]; | |
| 194 | + | |
| 195 | + int lastVoiceToChange; | |
| 196 | + | |
| 197 | + float pitchBend; | |
| 198 | + tRamp pitchBendRamp; | |
| 199 | + | |
| 200 | + int currentNote; | |
| 201 | + int currentVoice; | |
| 202 | + int currentVelocity; | |
| 203 | + int maxLength; | |
| 204 | + | |
| 205 | + } _tPoly; | |
| 206 | + | |
| 207 | + typedef _tPoly* tPoly; | |
| 208 | + | |
| 209 | + /* MPoly*/ | |
| 210 | + //! Initialize a tPoly to the default LEAF mempool. | |
| 211 | + /*! | |
| 212 | + @param poly A pointer to the tPoly to be initialized. | |
| 213 | + @param maxNumVoices The maximum number of voices this tPoly can handle at once. | |
| 214 | + */ | |
| 215 | + void tPoly_init (tPoly* const poly, int maxNumVoices); | |
| 216 | + | |
| 217 | + | |
| 218 | + //! Free a tPoly from the default LEAF mempool. | |
| 219 | + /*! | |
| 220 | + @param poly A pointer to the tPoly to be freed. | |
| 221 | + */ | |
| 222 | + void tPoly_free (tPoly* const poly); | |
| 223 | + | |
| 224 | + | |
| 225 | + //! Initialize a tPoly to a specified mempool. | |
| 226 | + /*! | |
| 227 | + @param poly A pointer to the tPoly to be initialized. | |
| 228 | + @param pool A pointer to the tMempool to which the tPoly should be initialized. | |
| 229 | + */ | |
| 230 | + void tPoly_initToPool (tPoly* const poly, int maxNumVoices, tMempool* const pool); | |
| 231 | + | |
| 232 | + | |
| 233 | + //! Free a tPoly from a specified mempool. | |
| 234 | + /*! | |
| 235 | + @param poly A pointer to the tPoly to be freed. | |
| 236 | + @param pool A pointer to the tMempool from which the tPoly should be freed. | |
| 237 | + */ | |
| 238 | + void tPoly_freeFromPool (tPoly* const poly, tMempool* const pool); | |
| 239 | + | |
| 240 | + //! Add a note with a given velocity to the poly handler. | |
| 241 | + /*! | |
| 242 | + @param poly A pointer to the relevant tPoly. | |
| 243 | + @param note The MIDI note number to add. | |
| 244 | + @param vel The MIDI velocity of the note to add. | |
| 245 | + @return The voice that will play the note. | |
| 246 | + */ | |
| 247 | + int tPoly_noteOn (tPoly* const poly, int note, uint8_t vel); | |
| 248 | + | |
| 249 | + | |
| 250 | + //! Remove a note from the poly handler. | |
| 251 | + /*! | |
| 252 | + @param poly A pointer to the relevant tPoly. | |
| 253 | + @param note The MIDI note number to remove. | |
| 254 | + @return The voice that was playing the removed note. | |
| 255 | + */ | |
| 256 | + int tPoly_noteOff (tPoly* const poly, uint8_t note); | |
| 257 | + | |
| 258 | + void tPoly_orderedAddToStack (tPoly* const poly, uint8_t note); | |
| 259 | + | |
| 260 | + | |
| 261 | + //! Set the number of voices available to play notes. | |
| 262 | + /*! | |
| 263 | + @param poly A pointer to the relevant tPoly. | |
| 264 | + @param numVoices The new number of available voices. Cannot be greater than the max number voices given in tPoly_init(). | |
| 265 | + */ | |
| 266 | + void tPoly_setNumVoices (tPoly* const poly, uint8_t numVoices); | |
| 267 | + | |
| 268 | + //! Set whether pitch glide over note changes in voices is active. | |
| 269 | + /*! | |
| 270 | + @param poly A pointer to the relevant tPoly. | |
| 271 | + @param isActive Whether pitch glide should be active or not. | |
| 272 | + */ | |
| 273 | + void tPoly_setPitchGlideActive (tPoly* const poly, oBool isActive); | |
| 274 | + | |
| 275 | + //! Set how long pitch glide over note changes in voices takes. | |
| 276 | + /*! | |
| 277 | + @param poly A pointer to the relevant tPoly. | |
| 278 | + @param t The time to glide in milliseconds. | |
| 279 | + */ | |
| 280 | + void tPoly_setPitchGlideTime (tPoly* const poly, float t); | |
| 281 | + | |
| 282 | + //! Set the amount of pitch bend | |
| 283 | + /*! | |
| 284 | + @param poly A pointer to the relevant tPoly. | |
| 285 | + @param pitchBend The new amount of pitch bend. | |
| 286 | + */ | |
| 287 | + void tPoly_setPitchBend (tPoly* const poly, float pitchBend); | |
| 288 | + void tPoly_setBendGlideTime (tPoly* const poly, float t); | |
| 289 | + void tPoly_setBendSamplesPerTick (tPoly* const poly, float t); | |
| 290 | + | |
| 291 | + | |
| 292 | + //! Execute all tick-rate changes in the poly handler's pitch, including glide and bend. | |
| 293 | + /*! | |
| 294 | + @param poly A pointer to the relevant tPoly. | |
| 295 | + */ | |
| 296 | + void tPoly_tickPitch (tPoly* const poly); | |
| 297 | + | |
| 298 | + | |
| 299 | + //! Execute the tick-rate change of the poly handler's pitch glide. | |
| 300 | + /*! | |
| 301 | + @param poly A pointer to the relevant tPoly. | |
| 302 | + */ | |
| 303 | + void tPoly_tickPitchGlide (tPoly* const poly); | |
| 304 | + | |
| 305 | + | |
| 306 | + //! Execute the tick-rate change of the poly handler's pitch bend. | |
| 307 | + /*! | |
| 308 | + @param poly A pointer to the relevant tPoly. | |
| 309 | + */ | |
| 310 | + void tPoly_tickPitchBend (tPoly* const poly); | |
| 311 | + | |
| 312 | + | |
| 313 | + //! Get the current number of voices available to play notes. | |
| 314 | + /*! | |
| 315 | + @param poly A pointer to the relevant tPoly. | |
| 316 | + @return The current number of voices available to play notes. | |
| 317 | + */ | |
| 318 | + int tPoly_getNumVoices (tPoly* const poly); | |
| 319 | + | |
| 320 | + //! Get the number of voices currently playing notes. | |
| 321 | + /*! | |
| 322 | + @param poly A pointer to the relevant tPoly. | |
| 323 | + @return The number of voices currently playing notes. | |
| 324 | + */ | |
| 325 | + int tPoly_getNumActiveVoices (tPoly* const poly); | |
| 326 | + | |
| 327 | + //! Get the current pitch of a given voice. | |
| 328 | + /*! | |
| 329 | + @param poly A pointer to the relevant tPoly. | |
| 330 | + @param voice The voice to get the pitch of. | |
| 331 | + @return The current pitch of the given voice as a fractional MIDI note number. | |
| 332 | + */ | |
| 333 | + float tPoly_getPitch (tPoly* const poly, uint8_t voice); | |
| 334 | + | |
| 335 | + | |
| 336 | + //! Get the current MIDI note number of a given voice. | |
| 337 | + /*! | |
| 338 | + @param poly A pointer to the relevant tPoly. | |
| 339 | + @param voice The voice to get the MIDI note number of. | |
| 340 | + @return The MIDI note number of the given voice. | |
| 341 | + */ | |
| 342 | + int tPoly_getKey (tPoly* const poly, uint8_t voice); | |
| 343 | + | |
| 344 | + | |
| 345 | + //! Get the current MIDI velocity of a given voice. | |
| 346 | + /*! | |
| 347 | + @param poly A pointer to the relevant tPoly. | |
| 348 | + @param voice The voice to get the MIDI velocity of. | |
| 349 | + @return The current MIDI velocity of the given voice. | |
| 350 | + */ | |
| 351 | + int tPoly_getVelocity (tPoly* const poly, uint8_t voice); | |
| 352 | + | |
| 353 | + | |
| 354 | + //! Get the current play state of a given voice. | |
| 355 | + /*! | |
| 356 | + @param poly A pointer to the relevant tPoly. | |
| 357 | + @param voice The voice to get the state of. | |
| 358 | + @return The current play state of the given voice. | |
| 359 | + */ | |
| 360 | + int tPoly_isOn (tPoly* const poly, uint8_t voice); | |
| 361 | + | |
| 362 | + /*! @} */ | |
| 363 | + | |
| 364 | + //============================================================================== | |
| 365 | + | |
| 366 | + | |
| 367 | + /* tPoly */ | |
| 368 | + typedef struct _tSimplePoly | |
| 369 | + { | |
| 370 | + tStack stack; | |
| 371 | + | |
| 372 | + int numVoices; | |
| 373 | + int maxNumVoices; | |
| 374 | + int** voices; | |
| 375 | + int stealing_on; | |
| 376 | + int recover_stolen; | |
| 377 | + | |
| 378 | + int notes[128][2]; | |
| 379 | + } _tSimplePoly; | |
| 380 | + | |
| 381 | + typedef _tSimplePoly* tSimplePoly; | |
| 382 | + | |
| 383 | + //! Initialize a tPoly to the default LEAF mempool. | |
| 384 | + /*! | |
| 385 | + @param poly A pointer to the tPoly to be initialized. | |
| 386 | + @param maxNumVoices The maximum number of voices this tPoly can handle at once. | |
| 387 | + */ | |
| 388 | + void tSimplePoly_init (tSimplePoly* const poly, int maxNumVoices); | |
| 389 | + | |
| 390 | + | |
| 391 | + //! Free a tPoly from the default LEAF mempool. | |
| 392 | + /*! | |
| 393 | + @param poly A pointer to the tPoly to be freed. | |
| 394 | + */ | |
| 395 | + void tSimplePoly_free (tSimplePoly* const poly); | |
| 396 | + | |
| 397 | + | |
| 398 | + //! Initialize a tPoly to a specified mempool. | |
| 399 | + /*! | |
| 400 | + @param poly A pointer to the tPoly to be initialized. | |
| 401 | + @param pool A pointer to the tMempool to which the tPoly should be initialized. | |
| 402 | + */ | |
| 403 | + void tSimplePoly_initToPool (tSimplePoly* const poly, int maxNumVoices, tMempool* const pool); | |
| 404 | + | |
| 405 | + | |
| 406 | + //! Free a tPoly from a specified mempool. | |
| 407 | + /*! | |
| 408 | + @param poly A pointer to the tPoly to be freed. | |
| 409 | + @param pool A pointer to the tMempool from which the tPoly should be freed. | |
| 410 | + */ | |
| 411 | + void tSimplePoly_freeFromPool (tSimplePoly* const poly, tMempool* const pool); | |
| 412 | + | |
| 413 | + //! Add a note with a given velocity to the poly handler. | |
| 414 | + /*! | |
| 415 | + @param poly A pointer to the relevant tPoly. | |
| 416 | + @param note The MIDI note number to add. | |
| 417 | + @param vel The MIDI velocity of the note to add. | |
| 418 | + @return The voice that will play the note. | |
| 419 | + */ | |
| 420 | + int tSimplePoly_noteOn (tSimplePoly* const poly, int note, uint8_t vel); | |
| 421 | + | |
| 422 | + | |
| 423 | + //! Remove a note from the poly handler. | |
| 424 | + /*! | |
| 425 | + @param poly A pointer to the relevant tPoly. | |
| 426 | + @param note The MIDI note number to remove. | |
| 427 | + @return The voice that was playing the removed note. | |
| 428 | + */ | |
| 429 | + int tSimplePoly_noteOff (tSimplePoly* const poly, uint8_t note); | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + void tSimplePoly_deactivateVoice(tSimplePoly* const polyh, uint8_t voice); | |
| 434 | + | |
| 435 | + int tSimplePoly_markPendingNoteOff(tSimplePoly* const polyh, uint8_t note); | |
| 436 | + | |
| 437 | + | |
| 438 | + //find if there is a voice with that note -- useful for note offs where you want to wait to remove it from the poly until the release phase of the envelope is finished | |
| 439 | + int tSimplePoly_findVoiceAssignedToNote(tSimplePoly* const polyh, uint8_t note); | |
| 440 | + | |
| 441 | + //! Set the number of voices available to play notes. | |
| 442 | + /*! | |
| 443 | + @param poly A pointer to the relevant tPoly. | |
| 444 | + @param numVoices The new number of available voices. Cannot be greater than the max number voices given in tPoly_init(). | |
| 445 | + */ | |
| 446 | + void tSimplePoly_setNumVoices (tSimplePoly* const poly, uint8_t numVoices); | |
| 447 | + | |
| 448 | + //! Set whether pitch glide over note changes in voices is active. | |
| 449 | + /*! | |
| 450 | + @param poly A pointer to the relevant tPoly. | |
| 451 | + @param isActive Whether pitch glide should be active or not. | |
| 452 | + */ | |
| 453 | + | |
| 454 | + //! Get the current number of voices available to play notes. | |
| 455 | + /*! | |
| 456 | + @param poly A pointer to the relevant tPoly. | |
| 457 | + @return The current number of voices available to play notes. | |
| 458 | + */ | |
| 459 | + int tSimplePoly_getNumVoices (tSimplePoly* const poly); | |
| 460 | + | |
| 461 | + //! Get the number of voices currently playing notes. | |
| 462 | + /*! | |
| 463 | + @param poly A pointer to the relevant tPoly. | |
| 464 | + @return The number of voices currently playing notes. | |
| 465 | + */ | |
| 466 | + int tSimplePoly_getNumActiveVoices (tSimplePoly* const poly); | |
| 467 | + | |
| 468 | + //! Get the current MIDI note number of a given voice. | |
| 469 | + /*! | |
| 470 | + @param poly A pointer to the relevant tPoly. | |
| 471 | + @param voice The voice to get the MIDI note number of. | |
| 472 | + @return The MIDI note number of the given voice. | |
| 473 | + */ | |
| 474 | + | |
| 475 | + int tSimplePoly_getPitch (tSimplePoly* const poly, uint8_t voice); | |
| 476 | + | |
| 477 | + | |
| 478 | + //this one returns negative one if the voice is inactive | |
| 479 | + int tSimplePoly_getPitchAndCheckActive(tSimplePoly* const polyh, uint8_t voice); | |
| 480 | + | |
| 481 | + | |
| 482 | + //! Get the current MIDI velocity of a given voice. | |
| 483 | + /*! | |
| 484 | + @param poly A pointer to the relevant tPoly. | |
| 485 | + @param voice The voice to get the MIDI velocity of. | |
| 486 | + @return The current MIDI velocity of the given voice. | |
| 487 | + */ | |
| 488 | + int tSimplePoly_getVelocity (tSimplePoly* const poly, uint8_t voice); | |
| 489 | + | |
| 490 | + | |
| 491 | + //! Get the current play state of a given voice. | |
| 492 | + /*! | |
| 493 | + @param poly A pointer to the relevant tPoly. | |
| 494 | + @param voice The voice to get the state of. | |
| 495 | + @return The current play state of the given voice. | |
| 496 | + */ | |
| 497 | + int tSimplePoly_isOn (tSimplePoly* const poly, uint8_t voice); | |
| 498 | + | |
| 499 | + /*! @} */ | |
| 500 | + //============================================================================== | |
| 501 | + | |
| 502 | +#ifdef __cplusplus | |
| 503 | +} | |
| 504 | +#endif | |
| 505 | + | |
| 506 | +#endif // LEAF_MIDI_H_INCLUDED | |
| 507 | + | |
| 508 | +//============================================================================== | |
| 509 | + | |
| 510 | + | |
| @@ -0,0 +1,1143 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + leaf-oscillators.h | |
| 3 | + Created: 20 Jan 2017 12:00:58pm | |
| 4 | + Author: Michael R Mulshine | |
| 5 | + ==============================================================================*/ | |
| 6 | + | |
| 7 | +#ifndef LEAF_OSCILLATORS_H_INCLUDED | |
| 8 | +#define LEAF_OSCILLATORS_H_INCLUDED | |
| 9 | + | |
| 10 | +#ifdef __cplusplus | |
| 11 | +extern "C" { | |
| 12 | +#endif | |
| 13 | + | |
| 14 | + //============================================================================== | |
| 15 | + | |
| 16 | +#include "leaf-math.h" | |
| 17 | +#include "leaf-mempool.h" | |
| 18 | +#include "leaf-tables.h" | |
| 19 | +#include "leaf-filters.h" | |
| 20 | + | |
| 21 | + /*! | |
| 22 | + * Header. | |
| 23 | + * @include basic-oscillators.h | |
| 24 | + * @example basic-oscillators.c | |
| 25 | + * An example. | |
| 26 | + */ | |
| 27 | + | |
| 28 | + //============================================================================== | |
| 29 | + | |
| 30 | + typedef struct _tCycle | |
| 31 | + { | |
| 32 | + // Underlying phasor | |
| 33 | + float phase; | |
| 34 | + float inc,freq; | |
| 35 | + } _tCycle; | |
| 36 | + | |
| 37 | + typedef _tCycle* tCycle; | |
| 38 | + | |
| 39 | + /*! | |
| 40 | + * @defgroup tcycle tCycle | |
| 41 | + * @ingroup oscillators | |
| 42 | + * @brief A cycle/sine waveform oscillator. Uses wavetable synthesis. | |
| 43 | + * @{ | |
| 44 | + */ | |
| 45 | + | |
| 46 | + //! Initialize a tCycle to the default LEAF mempool. | |
| 47 | + /*! | |
| 48 | + @param osc A pointer to the tCycle to be initialized. | |
| 49 | + */ | |
| 50 | + void tCycle_init (tCycle* const osc); | |
| 51 | + | |
| 52 | + | |
| 53 | + //! Free a tCycle from the default LEAF mempool. | |
| 54 | + /*! | |
| 55 | + @param osc A pointer to the tCycle to be freed. | |
| 56 | + */ | |
| 57 | + void tCycle_free (tCycle* const osc); | |
| 58 | + | |
| 59 | + | |
| 60 | + //! Initialize a tCycle to a specified mempool. | |
| 61 | + /*! | |
| 62 | + @param osc A pointer to the tCycle to be initialized. | |
| 63 | + @param pool A pointer to the tMempool to which the tCycle should be initialized. | |
| 64 | + */ | |
| 65 | + void tCycle_initToPool (tCycle* const osc, tMempool* const pool); | |
| 66 | + | |
| 67 | + | |
| 68 | + //! Free a tCycle from a specified mempool. | |
| 69 | + /*! | |
| 70 | + @param osc A pointer to the tCycle to be freed. | |
| 71 | + @param pool A pointer to the tMempool from which the tCycle should be freed. | |
| 72 | + */ | |
| 73 | + void tCycle_freeFromPool (tCycle* const osc, tMempool* const pool); | |
| 74 | + | |
| 75 | + | |
| 76 | + //! Tick a tCycle oscillator. | |
| 77 | + /*! | |
| 78 | + @param osc A pointer to the relevant tCycle. | |
| 79 | + @return The ticked sample as a float from -1 to 1. | |
| 80 | + */ | |
| 81 | + float tCycle_tick (tCycle* const osc); | |
| 82 | + | |
| 83 | + | |
| 84 | + //! Set the frequency of a tCycle oscillator. | |
| 85 | + /*! | |
| 86 | + @param osc A pointer to the relevant tCycle. | |
| 87 | + @param freq The frequency to set the oscillator to. | |
| 88 | + */ | |
| 89 | + void tCycle_setFreq (tCycle* const osc, float freq); | |
| 90 | + | |
| 91 | + /*! @} */ | |
| 92 | + | |
| 93 | + //============================================================================== | |
| 94 | + | |
| 95 | + /* tTriangle: Anti-aliased Triangle waveform using wavetable interpolation. Wavetables constructed from sine components. */ | |
| 96 | + typedef struct _tTriangle | |
| 97 | + { | |
| 98 | + // Underlying phasor | |
| 99 | + float phase; | |
| 100 | + float inc,freq; | |
| 101 | + int oct; | |
| 102 | + float w; | |
| 103 | + } _tTriangle; | |
| 104 | + | |
| 105 | + typedef _tTriangle* tTriangle; | |
| 106 | + | |
| 107 | + /*! | |
| 108 | + * @defgroup ttriangle tTriangle | |
| 109 | + * @ingroup oscillators | |
| 110 | + * @brief An anti-aliased triangle waveform oscillator. Uses wavetable synthesis. | |
| 111 | + * @{ | |
| 112 | + */ | |
| 113 | + | |
| 114 | + //! Initialize a tTriangle to the default LEAF mempool. | |
| 115 | + /*! | |
| 116 | + @param osc A pointer to the tTriangle to be initialized. | |
| 117 | + */ | |
| 118 | + void tTriangle_init (tTriangle* const osc); | |
| 119 | + | |
| 120 | + | |
| 121 | + //! Free a tTriangle from the default LEAF mempool. | |
| 122 | + /*! | |
| 123 | + @param osc A pointer to the tTriangle to be freed. | |
| 124 | + */ | |
| 125 | + void tTriangle_free (tTriangle* const osc); | |
| 126 | + | |
| 127 | + | |
| 128 | + //! Initialize a tTriangle to a specified mempool. | |
| 129 | + /*! | |
| 130 | + @param osc A pointer to the tTriangle to be initialized. | |
| 131 | + @param pool A pointer to the tMempool to which the tTriangle should be initialized. | |
| 132 | + */ | |
| 133 | + void tTriangle_initToPool (tTriangle* const osc, tMempool* const pool); | |
| 134 | + | |
| 135 | + | |
| 136 | + //! Free a tTriangle from a specified mempool. | |
| 137 | + /*! | |
| 138 | + @param osc A pointer to the tTriangle to be freed. | |
| 139 | + @param pool A pointer to the tMempool from which the tTriangle should be freed. | |
| 140 | + */ | |
| 141 | + void tTriangle_freeFromPool (tTriangle* const osc, tMempool* const pool); | |
| 142 | + | |
| 143 | + | |
| 144 | + //! Tick a tTriangle oscillator. | |
| 145 | + /*! | |
| 146 | + @param osc A pointer to the relevant tTriangle. | |
| 147 | + @return The ticked sample as a float from -1 to 1. | |
| 148 | + */ | |
| 149 | + float tTriangle_tick (tTriangle* const osc); | |
| 150 | + | |
| 151 | + | |
| 152 | + //! Set the frequency of a tTriangle oscillator. | |
| 153 | + /*! | |
| 154 | + @param osc A pointer to the relevant tTriangle. | |
| 155 | + @param freq The frequency to set the oscillator to. | |
| 156 | + */ | |
| 157 | + void tTriangle_setFreq (tTriangle* const osc, float freq); | |
| 158 | + | |
| 159 | + /*! @} */ | |
| 160 | + | |
| 161 | + //============================================================================== | |
| 162 | + | |
| 163 | + /* tSquare: Anti-aliased Square waveform using wavetable interpolation. Wavetables constructed from sine components. */ | |
| 164 | + typedef struct _tSquare | |
| 165 | + { | |
| 166 | + // Underlying phasor | |
| 167 | + float phase; | |
| 168 | + float inc,freq; | |
| 169 | + int oct; | |
| 170 | + float w; | |
| 171 | + } _tSquare; | |
| 172 | + | |
| 173 | + typedef _tSquare* tSquare; | |
| 174 | + | |
| 175 | + /*! | |
| 176 | + * @defgroup tsquare tSquare | |
| 177 | + * @ingroup oscillators | |
| 178 | + * @brief An anti-aliased square waveform oscillator. Uses wavetable synthesis. | |
| 179 | + * @{ | |
| 180 | + */ | |
| 181 | + | |
| 182 | + //! Initialize a tSquare to the default LEAF mempool. | |
| 183 | + /*! | |
| 184 | + @param osc A pointer to the tSquare to be initialized. | |
| 185 | + */ | |
| 186 | + void tSquare_init (tSquare* const osc); | |
| 187 | + | |
| 188 | + | |
| 189 | + //! Free a tSquare from the default LEAF mempool. | |
| 190 | + /*! | |
| 191 | + @param osc A pointer to the tSquare to be freed. | |
| 192 | + */ | |
| 193 | + void tSquare_free (tSquare* const osc); | |
| 194 | + | |
| 195 | + | |
| 196 | + //! Initialize a tSquare to a specified mempool. | |
| 197 | + /*! | |
| 198 | + @param osc A pointer to the tSquare to be initialized. | |
| 199 | + @param pool A pointer to the tMempool to which the tSquare should be initialized. | |
| 200 | + */ | |
| 201 | + void tSquare_initToPool (tSquare* const osc, tMempool* const); | |
| 202 | + | |
| 203 | + | |
| 204 | + //! Free a tSquare from a specified mempool. | |
| 205 | + /*! | |
| 206 | + @param osc A pointer to the tSquare to be freed. | |
| 207 | + @param pool A pointer to the tMempool from which the tSquare should be freed. | |
| 208 | + */ | |
| 209 | + void tSquare_freeFromPool(tSquare* const osc, tMempool* const); | |
| 210 | + | |
| 211 | + | |
| 212 | + //! Tick a tSquare oscillator. | |
| 213 | + /*! | |
| 214 | + @param osc A pointer to the relevant tSquare. | |
| 215 | + @return The ticked sample as a float from -1 to 1. | |
| 216 | + */ | |
| 217 | + float tSquare_tick (tSquare* const osc); | |
| 218 | + | |
| 219 | + | |
| 220 | + //! Set the frequency of a tSquare oscillator. | |
| 221 | + /*! | |
| 222 | + @param osc A pointer to the relevant tSquare. | |
| 223 | + @param freq The frequency to set the oscillator to. | |
| 224 | + */ | |
| 225 | + void tSquare_setFreq (tSquare* const osc, float freq); | |
| 226 | + | |
| 227 | + /*! @} */ | |
| 228 | + | |
| 229 | + //============================================================================== | |
| 230 | + | |
| 231 | + /* tSawtooth: Anti-aliased Sawtooth waveform using wavetable interpolation. Wavetables constructed from sine components. */ | |
| 232 | + typedef struct _tSawtooth | |
| 233 | + { | |
| 234 | + // Underlying phasor | |
| 235 | + float phase; | |
| 236 | + float inc,freq; | |
| 237 | + int oct; | |
| 238 | + float w; | |
| 239 | + } _tSawtooth; | |
| 240 | + | |
| 241 | + typedef _tSawtooth* tSawtooth; | |
| 242 | + | |
| 243 | + /*! | |
| 244 | + * @defgroup tsawtooth tSawtooth | |
| 245 | + * @ingroup oscillators | |
| 246 | + * @brief An anti-aliased sawtooth waveform oscillator. Uses wavetable synthesis. | |
| 247 | + * @{ | |
| 248 | + */ | |
| 249 | + | |
| 250 | + //! Initialize a tSawtooth to the default LEAF mempool. | |
| 251 | + /*! | |
| 252 | + @param osc A pointer to the tSawtooth to be initialized. | |
| 253 | + */ | |
| 254 | + void tSawtooth_init (tSawtooth* const osc); | |
| 255 | + | |
| 256 | + | |
| 257 | + //! Free a tSawtooth from the default LEAF mempool. | |
| 258 | + /*! | |
| 259 | + @param osc A pointer to the tSawtooth to be freed. | |
| 260 | + */ | |
| 261 | + void tSawtooth_free (tSawtooth* const osc); | |
| 262 | + | |
| 263 | + | |
| 264 | + //! Initialize a tSawtooth to a specified mempool. | |
| 265 | + /*! | |
| 266 | + @param osc A pointer to the tSawtooth to be initialized. | |
| 267 | + @param pool A pointer to the tMempool to which the tSawtooth should be initialized. | |
| 268 | + */ | |
| 269 | + void tSawtooth_initToPool (tSawtooth* const osc, tMempool* const pool); | |
| 270 | + | |
| 271 | + | |
| 272 | + //! Free a tSawtooth from a specified mempool. | |
| 273 | + /*! | |
| 274 | + @param osc A pointer to the tSawtooth to be freed. | |
| 275 | + @param pool A pointer to the tMempool from which the tSawtooth should be freed. | |
| 276 | + */ | |
| 277 | + void tSawtooth_freeFromPool (tSawtooth* const osc, tMempool* const pool); | |
| 278 | + | |
| 279 | + | |
| 280 | + //! Tick a tSawtooth oscillator. | |
| 281 | + /*! | |
| 282 | + @param osc A pointer to the relevant tSawtooth. | |
| 283 | + @return The ticked sample as a float from -1 to 1. | |
| 284 | + */ | |
| 285 | + float tSawtooth_tick (tSawtooth* const osc); | |
| 286 | + | |
| 287 | + | |
| 288 | + //! Set the frequency of a tSawtooth oscillator. | |
| 289 | + /*! | |
| 290 | + @param osc A pointer to the relevant tSawtooth. | |
| 291 | + @param freq The frequency to set the oscillator to. | |
| 292 | + */ | |
| 293 | + void tSawtooth_setFreq (tSawtooth* const osc, float freq); | |
| 294 | + | |
| 295 | + /*! @} */ | |
| 296 | + | |
| 297 | + //============================================================================== | |
| 298 | + | |
| 299 | + typedef struct _tSine | |
| 300 | + { | |
| 301 | + float* sine; | |
| 302 | + int size; | |
| 303 | + float phase; | |
| 304 | + float inc,freq; | |
| 305 | + } _tSine; | |
| 306 | + | |
| 307 | + typedef _tSine* tSine; | |
| 308 | + | |
| 309 | + /*! | |
| 310 | + * @defgroup tsine tSine | |
| 311 | + * @ingroup oscillators | |
| 312 | + * @brief A cycle/sine waveform oscillator. | |
| 313 | + * @{ | |
| 314 | + */ | |
| 315 | + | |
| 316 | + //! Initialize a tSine to the default LEAF mempool. | |
| 317 | + /*! | |
| 318 | + @param osc A pointer to the tSine to be initialized. | |
| 319 | + */ | |
| 320 | + void tSine_init (tSine* const osc, int size); | |
| 321 | + | |
| 322 | + | |
| 323 | + //! Free a tSine from the default LEAF mempool. | |
| 324 | + /*! | |
| 325 | + @param osc A pointer to the tSine to be freed. | |
| 326 | + */ | |
| 327 | + void tSine_free (tSine* const osc); | |
| 328 | + | |
| 329 | + | |
| 330 | + //! Initialize a tSine to a specified mempool. | |
| 331 | + /*! | |
| 332 | + @param osc A pointer to the tSine to be initialized. | |
| 333 | + @param pool A pointer to the tMempool to which the tSine should be initialized. | |
| 334 | + */ | |
| 335 | + void tSine_initToPool (tSine* const osc, int size, tMempool* const pool); | |
| 336 | + | |
| 337 | + | |
| 338 | + //! Free a tSine from a specified mempool. | |
| 339 | + /*! | |
| 340 | + @param osc A pointer to the tSine to be freed. | |
| 341 | + @param pool A pointer to the tMempool from which the tSine should be freed. | |
| 342 | + */ | |
| 343 | + void tSine_freeFromPool (tSine* const osc, tMempool* const pool); | |
| 344 | + | |
| 345 | + | |
| 346 | + //! Tick a tSine oscillator. | |
| 347 | + /*! | |
| 348 | + @param osc A pointer to the relevant tSine. | |
| 349 | + @return The ticked sample as a float from -1 to 1. | |
| 350 | + */ | |
| 351 | + float tSine_tick (tSine* const osc); | |
| 352 | + | |
| 353 | + | |
| 354 | + //! Set the frequency of a tSine oscillator. | |
| 355 | + /*! | |
| 356 | + @param osc A pointer to the relevant tSine. | |
| 357 | + @param freq The frequency to set the oscillator to. | |
| 358 | + */ | |
| 359 | + void tSine_setFreq (tSine* const osc, float freq); | |
| 360 | + | |
| 361 | + /*! @} */ | |
| 362 | + | |
| 363 | + //============================================================================== | |
| 364 | + | |
| 365 | + /* tTri: Anti-aliased Triangle waveform using wavetable interpolation. */ | |
| 366 | + typedef struct _tTri | |
| 367 | + { | |
| 368 | + float phase; | |
| 369 | + float inc,freq; | |
| 370 | + float skew; | |
| 371 | + float lastOut; | |
| 372 | + } _tTri; | |
| 373 | + | |
| 374 | + typedef _tTri* tTri; | |
| 375 | + | |
| 376 | + /*! | |
| 377 | + * @defgroup tTri tTri | |
| 378 | + * @ingroup oscillators | |
| 379 | + * @brief An anti-aliased triangle waveform oscillator. | |
| 380 | + * @{ | |
| 381 | + */ | |
| 382 | + | |
| 383 | + //! Initialize a tTri to the default LEAF mempool. | |
| 384 | + /*! | |
| 385 | + @param osc A pointer to the tTri to be initialized. | |
| 386 | + */ | |
| 387 | + void tTri_init (tTri* const osc); | |
| 388 | + | |
| 389 | + | |
| 390 | + //! Free a tTri from the default LEAF mempool. | |
| 391 | + /*! | |
| 392 | + @param osc A pointer to the tTri to be freed. | |
| 393 | + */ | |
| 394 | + void tTri_free (tTri* const osc); | |
| 395 | + | |
| 396 | + | |
| 397 | + //! Initialize a tTri to a specified mempool. | |
| 398 | + /*! | |
| 399 | + @param osc A pointer to the tTri to be initialized. | |
| 400 | + @param pool A pointer to the tMempool to which the tTri should be initialized. | |
| 401 | + */ | |
| 402 | + void tTri_initToPool (tTri* const osc, tMempool* const pool); | |
| 403 | + | |
| 404 | + | |
| 405 | + //! Free a tTri from a specified mempool. | |
| 406 | + /*! | |
| 407 | + @param osc A pointer to the tTri to be freed. | |
| 408 | + @param pool A pointer to the tMempool from which the tTri should be freed. | |
| 409 | + */ | |
| 410 | + void tTri_freeFromPool (tTri* const osc, tMempool* const pool); | |
| 411 | + | |
| 412 | + | |
| 413 | + //! Tick a tTri oscillator. | |
| 414 | + /*! | |
| 415 | + @param osc A pointer to the relevant tTri. | |
| 416 | + @return The ticked sample as a float from -1 to 1. | |
| 417 | + */ | |
| 418 | + float tTri_tick (tTri* const osc); | |
| 419 | + | |
| 420 | + | |
| 421 | + //! Set the frequency of a tTri oscillator. | |
| 422 | + /*! | |
| 423 | + @param osc A pointer to the relevant tTriangle. | |
| 424 | + @param freq The frequency to set the oscillator to. | |
| 425 | + */ | |
| 426 | + void tTri_setFreq (tTri* const osc, float freq); | |
| 427 | + | |
| 428 | + void tTri_setSkew (tTri* const osc, float skew); | |
| 429 | + | |
| 430 | + /*! @} */ | |
| 431 | + | |
| 432 | + //============================================================================== | |
| 433 | + | |
| 434 | + /* tPulse: Anti-aliased Square waveform. */ | |
| 435 | + typedef struct _tPulse | |
| 436 | + { | |
| 437 | + float phase; | |
| 438 | + float inc,freq; | |
| 439 | + float width; | |
| 440 | + } _tPulse; | |
| 441 | + | |
| 442 | + typedef _tPulse* tPulse; | |
| 443 | + | |
| 444 | + /*! | |
| 445 | + * @defgroup tPulse tPulse | |
| 446 | + * @ingroup oscillators | |
| 447 | + * @brief An anti-aliased pulse waveform oscillator with changeable pulse width. | |
| 448 | + * @{ | |
| 449 | + */ | |
| 450 | + | |
| 451 | + //! Initialize a tPulse to the default LEAF mempool. | |
| 452 | + /*! | |
| 453 | + @param osc A pointer to the tPulse to be initialized. | |
| 454 | + */ | |
| 455 | + void tPulse_init (tPulse* const osc); | |
| 456 | + | |
| 457 | + | |
| 458 | + //! Free a tPulse from the default LEAF mempool. | |
| 459 | + /*! | |
| 460 | + @param osc A pointer to the tPulse to be freed. | |
| 461 | + */ | |
| 462 | + void tPulse_free (tPulse* const osc); | |
| 463 | + | |
| 464 | + | |
| 465 | + //! Initialize a tPulse to a specified mempool. | |
| 466 | + /*! | |
| 467 | + @param osc A pointer to the tPulse to be initialized. | |
| 468 | + @param pool A pointer to the tMempool to which the tPulse should be initialized. | |
| 469 | + */ | |
| 470 | + void tPulse_initToPool (tPulse* const osc, tMempool* const); | |
| 471 | + | |
| 472 | + | |
| 473 | + //! Free a tPulse from a specified mempool. | |
| 474 | + /*! | |
| 475 | + @param osc A pointer to the tPulse to be freed. | |
| 476 | + @param pool A pointer to the tMempool from which the tPulse should be freed. | |
| 477 | + */ | |
| 478 | + void tPulse_freeFromPool(tPulse* const osc, tMempool* const); | |
| 479 | + | |
| 480 | + | |
| 481 | + //! Tick a tPulse oscillator. | |
| 482 | + /*! | |
| 483 | + @param osc A pointer to the relevant tPulse. | |
| 484 | + @return The ticked sample as a float from -1 to 1. | |
| 485 | + */ | |
| 486 | + float tPulse_tick (tPulse* const osc); | |
| 487 | + | |
| 488 | + | |
| 489 | + //! Set the frequency of a tPulse oscillator. | |
| 490 | + /*! | |
| 491 | + @param osc A pointer to the relevant tPulse. | |
| 492 | + @param freq The frequency to set the oscillator to. | |
| 493 | + */ | |
| 494 | + void tPulse_setFreq (tPulse* const osc, float freq); | |
| 495 | + | |
| 496 | + void tPulse_setWidth (tPulse* const osc, float width); | |
| 497 | + | |
| 498 | + /*! @} */ | |
| 499 | + | |
| 500 | + //============================================================================== | |
| 501 | + | |
| 502 | + /* tSawtooth: Anti-aliased Sawtooth waveform. */ | |
| 503 | + typedef struct _tSaw | |
| 504 | + { | |
| 505 | + float phase; | |
| 506 | + float inc,freq; | |
| 507 | + } _tSaw; | |
| 508 | + | |
| 509 | + typedef _tSaw* tSaw; | |
| 510 | + | |
| 511 | + /*! | |
| 512 | + * @defgroup tsaw tSaw | |
| 513 | + * @ingroup oscillators | |
| 514 | + * @brief An anti-aliased sawtooth waveform oscillator. Uses wavetable synthesis. | |
| 515 | + * @{ | |
| 516 | + */ | |
| 517 | + | |
| 518 | + //! Initialize a tSaw to the default LEAF mempool. | |
| 519 | + /*! | |
| 520 | + @param osc A pointer to the tSaw to be initialized. | |
| 521 | + */ | |
| 522 | + void tSaw_init (tSaw* const osc); | |
| 523 | + | |
| 524 | + | |
| 525 | + //! Free a tSaw from the default LEAF mempool. | |
| 526 | + /*! | |
| 527 | + @param osc A pointer to the tSaw to be freed. | |
| 528 | + */ | |
| 529 | + void tSaw_free (tSaw* const osc); | |
| 530 | + | |
| 531 | + | |
| 532 | + //! Initialize a tSaw to a specified mempool. | |
| 533 | + /*! | |
| 534 | + @param osc A pointer to the tSaw to be initialized. | |
| 535 | + @param pool A pointer to the tMempool to which the tSaw should be initialized. | |
| 536 | + */ | |
| 537 | + void tSaw_initToPool (tSaw* const osc, tMempool* const pool); | |
| 538 | + | |
| 539 | + | |
| 540 | + //! Free a tSaw from a specified mempool. | |
| 541 | + /*! | |
| 542 | + @param osc A pointer to the tSaw to be freed. | |
| 543 | + @param pool A pointer to the tMempool from which the tSaw should be freed. | |
| 544 | + */ | |
| 545 | + void tSaw_freeFromPool (tSaw* const osc, tMempool* const pool); | |
| 546 | + | |
| 547 | + | |
| 548 | + //! Tick a tSaw oscillator. | |
| 549 | + /*! | |
| 550 | + @param osc A pointer to the relevant tSaw. | |
| 551 | + @return The ticked sample as a float from -1 to 1. | |
| 552 | + */ | |
| 553 | + float tSaw_tick (tSaw* const osc); | |
| 554 | + | |
| 555 | + | |
| 556 | + //! Set the frequency of a tSaw oscillator. | |
| 557 | + /*! | |
| 558 | + @param osc A pointer to the relevant tSaw. | |
| 559 | + @param freq The frequency to set the oscillator to. | |
| 560 | + */ | |
| 561 | + void tSaw_setFreq (tSaw* const osc, float freq); | |
| 562 | + | |
| 563 | + /*! @} */ | |
| 564 | + | |
| 565 | + //============================================================================== | |
| 566 | + | |
| 567 | + /* tPhasor: Aliasing phasor [0.0, 1.0) */ | |
| 568 | + typedef struct _tPhasor | |
| 569 | + { | |
| 570 | + float phase; | |
| 571 | + float inc,freq; | |
| 572 | + uint8_t phaseDidReset; | |
| 573 | + | |
| 574 | + } _tPhasor; | |
| 575 | + | |
| 576 | + typedef _tPhasor* tPhasor; | |
| 577 | + | |
| 578 | + /*! | |
| 579 | + * @defgroup tphasor Phasor | |
| 580 | + * @ingroup oscillators | |
| 581 | + * @brief An aliasing phasor. | |
| 582 | + * @{ | |
| 583 | + */ | |
| 584 | + | |
| 585 | + //! Initialize a tPhasor to the default LEAF mempool. | |
| 586 | + /*! | |
| 587 | + @param osc A pointer to the tPhasor to be initialized. | |
| 588 | + */ | |
| 589 | + void tPhasor_init (tPhasor* const osc); | |
| 590 | + | |
| 591 | + | |
| 592 | + //! Free a tPhasor from the default LEAF mempool. | |
| 593 | + /*! | |
| 594 | + @param osc A pointer to the tPhasor to be freed. | |
| 595 | + */ | |
| 596 | + void tPhasor_free (tPhasor* const osc); | |
| 597 | + | |
| 598 | + | |
| 599 | + //! Initialize a tPhasor to a specified mempool. | |
| 600 | + /*! | |
| 601 | + @param osc A pointer to the tPhasor to be initialized. | |
| 602 | + @param pool A pointer to the tMempool to which the tPhasor should be initialized. | |
| 603 | + */ | |
| 604 | + void tPhasor_initToPool (tPhasor* const osc, tMempool* const); | |
| 605 | + | |
| 606 | + | |
| 607 | + //! Free a tPhasor from a specified mempool. | |
| 608 | + /*! | |
| 609 | + @param osc A pointer to the tPhasor to be freed. | |
| 610 | + @param pool A pointer to the tMempool from which the tPhasor should be freed. | |
| 611 | + */ | |
| 612 | + void tPhasor_freeFromPool(tPhasor* const osc, tMempool* const); | |
| 613 | + | |
| 614 | + | |
| 615 | + //! Tick a tPhasor oscillator. | |
| 616 | + /*! | |
| 617 | + @param osc A pointer to the relevant tPhasor. | |
| 618 | + @return The ticked sample as a float from 0 to 1. | |
| 619 | + */ | |
| 620 | + float tPhasor_tick (tPhasor* const osc); | |
| 621 | + | |
| 622 | + | |
| 623 | + //! Set the frequency of a tPhasor oscillator. | |
| 624 | + /*! | |
| 625 | + @param osc A pointer to the relevant tPhasor. | |
| 626 | + @param freq The frequency to set the oscillator to. | |
| 627 | + */ | |
| 628 | + void tPhasor_setFreq (tPhasor* const osc, float freq); | |
| 629 | + | |
| 630 | + /*! @} */ | |
| 631 | + | |
| 632 | + //============================================================================== | |
| 633 | + | |
| 634 | + /*! | |
| 635 | + * @defgroup tnoise tNoise | |
| 636 | + * @ingroup oscillators | |
| 637 | + * @brief A noise generator. | |
| 638 | + * @{ | |
| 639 | + */ | |
| 640 | + | |
| 641 | + /* tNoise. WhiteNoise, PinkNoise. */ | |
| 642 | + /*! | |
| 643 | + * Noise types | |
| 644 | + */ | |
| 645 | + enum NoiseType | |
| 646 | + { | |
| 647 | + WhiteNoise, //!< White noise. Full spectrum. | |
| 648 | + PinkNoise, //!< Pink noise. Inverse frequency-proportional spectrum. | |
| 649 | + NoiseTypeNil, | |
| 650 | + }; | |
| 651 | + | |
| 652 | + typedef enum NoiseType NoiseType; | |
| 653 | + | |
| 654 | + typedef struct _tNoise | |
| 655 | + { | |
| 656 | + NoiseType type; | |
| 657 | + float pinkb0, pinkb1, pinkb2; | |
| 658 | + float(*rand)(void); | |
| 659 | + | |
| 660 | + } _tNoise; | |
| 661 | + | |
| 662 | + typedef _tNoise* tNoise; | |
| 663 | + | |
| 664 | + //! Initialize a tNoise to the default LEAF mempool. | |
| 665 | + /*! | |
| 666 | + @param osc A pointer to the tNoise to be initialized. | |
| 667 | + */ | |
| 668 | + void tNoise_init (tNoise* const noise, NoiseType type); | |
| 669 | + | |
| 670 | + | |
| 671 | + //! Free a tNoise from the default LEAF mempool. | |
| 672 | + /*! | |
| 673 | + @param osc A pointer to the tNoise to be freed. | |
| 674 | + */ | |
| 675 | + void tNoise_free (tNoise* const noise); | |
| 676 | + | |
| 677 | + | |
| 678 | + //! Initialize a tNoise to a specified mempool. | |
| 679 | + /*! | |
| 680 | + @param osc A pointer to the tNoise to be initialized. | |
| 681 | + @param pool A pointer to the tMempool to which the tNoise should be initialized. | |
| 682 | + */ | |
| 683 | + void tNoise_initToPool (tNoise* const noise, NoiseType type, tMempool* const); | |
| 684 | + | |
| 685 | + | |
| 686 | + //! Free a tNoise from a specified mempool. | |
| 687 | + /*! | |
| 688 | + @param osc A pointer to the tNoise to be freed. | |
| 689 | + @param pool A pointer to the tMempool from which the tNoise should be freed. | |
| 690 | + */ | |
| 691 | + void tNoise_freeFromPool (tNoise* const noise, tMempool* const); | |
| 692 | + | |
| 693 | + | |
| 694 | + //! Tick a tNoise oscillator. | |
| 695 | + /*! | |
| 696 | + @param osc A pointer to the relevant tNoise. | |
| 697 | + @return The ticked sample as a float from -1 to 1. | |
| 698 | + */ | |
| 699 | + float tNoise_tick (tNoise* const noise); | |
| 700 | + | |
| 701 | + /*! @} */ | |
| 702 | + | |
| 703 | + //============================================================================== | |
| 704 | + | |
| 705 | + /*! | |
| 706 | + * @defgroup tneuron tNeuron | |
| 707 | + * @ingroup oscillators | |
| 708 | + * @brief A model of a neuron, adapted to act as an oscillator. | |
| 709 | + * @{ | |
| 710 | + */ | |
| 711 | + | |
| 712 | + /*! | |
| 713 | + * Neuron modes | |
| 714 | + */ | |
| 715 | + enum NeuronMode | |
| 716 | + { | |
| 717 | + NeuronNormal, //!< Normal operation | |
| 718 | + NeuronTanh, //!< Tanh voltage shaping | |
| 719 | + NeuronAaltoShaper, //!< Aalto voltage shaping | |
| 720 | + NeuronModeNil | |
| 721 | + }; | |
| 722 | + typedef enum NeuronMode NeuronMode; | |
| 723 | + | |
| 724 | + typedef struct _tNeuron | |
| 725 | + { | |
| 726 | + tPoleZero f; | |
| 727 | + | |
| 728 | + NeuronMode mode; | |
| 729 | + | |
| 730 | + float voltage, current; | |
| 731 | + float timeStep; | |
| 732 | + | |
| 733 | + float alpha[3]; | |
| 734 | + float beta[3]; | |
| 735 | + float rate[3]; | |
| 736 | + float V[3]; | |
| 737 | + float P[3]; | |
| 738 | + float gK, gN, gL, C; | |
| 739 | + } _tNeuron; | |
| 740 | + | |
| 741 | + typedef _tNeuron* tNeuron; | |
| 742 | + | |
| 743 | + //! Initialize a tNeuron to the default LEAF mempool. | |
| 744 | + /*! | |
| 745 | + @param neuron A pointer to the tNeuron to be initialized. | |
| 746 | + */ | |
| 747 | + void tNeuron_init (tNeuron* const neuron); | |
| 748 | + | |
| 749 | + | |
| 750 | + //! Free a tNeuron from the default LEAF mempool. | |
| 751 | + /*! | |
| 752 | + @param neuron A pointer to the tNeuron to be freed. | |
| 753 | + */ | |
| 754 | + void tNeuron_free (tNeuron* const neuron); | |
| 755 | + | |
| 756 | + | |
| 757 | + //! Initialize a tNeuron to a specified mempool. | |
| 758 | + /*! | |
| 759 | + @param neuron A pointer to the tNeuron to be initialized. | |
| 760 | + @param pool A pointer to the tMempool to which the tNeuron should be initialized. | |
| 761 | + */ | |
| 762 | + void tNeuron_initToPool (tNeuron* const neuron, tMempool* const pool); | |
| 763 | + | |
| 764 | + | |
| 765 | + //! Free a tNeuron from a specified mempool. | |
| 766 | + /*! | |
| 767 | + @param neuron A pointer to the tNeuron to be freed. | |
| 768 | + @param pool A pointer to the tMempool from which the tNeuron should be freed. | |
| 769 | + */ | |
| 770 | + void tNeuron_freeFromPool(tNeuron* const neuron, tMempool* const pool); | |
| 771 | + | |
| 772 | + | |
| 773 | + //! Reset the neuron model. | |
| 774 | + /*! | |
| 775 | + @param neuron A pointer to the relevant tNeuron. | |
| 776 | + */ | |
| 777 | + void tNeuron_reset (tNeuron* const neuron); | |
| 778 | + | |
| 779 | + | |
| 780 | + //! Tick a tNeuron oscillator. | |
| 781 | + /*! | |
| 782 | + @param neuron A pointer to the relevant tNeuron. | |
| 783 | + @return The ticked sample as a float from -1 to 1. | |
| 784 | + */ | |
| 785 | + float tNeuron_tick (tNeuron* const neuron); | |
| 786 | + | |
| 787 | + | |
| 788 | + //! Set the tNeuron shaping mode. | |
| 789 | + /*! | |
| 790 | + @param neuron A pointer to the relevant tNeuron. | |
| 791 | + @param mode The mode to set the tNeuron to. | |
| 792 | + */ | |
| 793 | + void tNeuron_setMode (tNeuron* const neuron, NeuronMode mode); | |
| 794 | + | |
| 795 | + | |
| 796 | + //! Set the current. | |
| 797 | + /*! | |
| 798 | + @param neuron A pointer to the relevant tNeuron. | |
| 799 | + @param current The new current. | |
| 800 | + */ | |
| 801 | + void tNeuron_setCurrent (tNeuron* const neuron, float current); | |
| 802 | + | |
| 803 | + | |
| 804 | + //! Set the potassium value. | |
| 805 | + /*! | |
| 806 | + @param neuron A pointer to the relevant tNeuron. | |
| 807 | + @param K The new potassium. | |
| 808 | + */ | |
| 809 | + void tNeuron_setK (tNeuron* const neuron, float K); | |
| 810 | + | |
| 811 | + | |
| 812 | + //! Set the chloride value. | |
| 813 | + /*! | |
| 814 | + @param neuron A pointer to the relevant tNeuron. | |
| 815 | + @param L The new chloride value. | |
| 816 | + */ | |
| 817 | + void tNeuron_setL (tNeuron* const neuron, float L); | |
| 818 | + | |
| 819 | + | |
| 820 | + //! Set the sodium value. | |
| 821 | + /*! | |
| 822 | + @param neuron A pointer to the relevant tNeuron. | |
| 823 | + @param N The new sodium value. | |
| 824 | + */ | |
| 825 | + void tNeuron_setN (tNeuron* const neuron, float N); | |
| 826 | + | |
| 827 | + | |
| 828 | + //! Set the calcium value. | |
| 829 | + /*! | |
| 830 | + @param neuron A pointer to the relevant tNeuron. | |
| 831 | + @param C The new calcium. | |
| 832 | + */ | |
| 833 | + void tNeuron_setC (tNeuron* const neuron, float C); | |
| 834 | + | |
| 835 | + | |
| 836 | + //! Set the V1 value. | |
| 837 | + /*! | |
| 838 | + @param neuron A pointer to the relevant tNeuron. | |
| 839 | + @param V1 The new V1. | |
| 840 | + */ | |
| 841 | + void tNeuron_setV1 (tNeuron* const neuron, float V1); | |
| 842 | + | |
| 843 | + | |
| 844 | + //! Set the V2 value. | |
| 845 | + /*! | |
| 846 | + @param neuron A pointer to the relevant tNeuron. | |
| 847 | + @param V2 The new V2. | |
| 848 | + */ | |
| 849 | + void tNeuron_setV2 (tNeuron* const neuron, float V2); | |
| 850 | + | |
| 851 | + | |
| 852 | + //! Set the V3 value. | |
| 853 | + /*! | |
| 854 | + @param neuron A pointer to the relevant tNeuron. | |
| 855 | + @param V3 The new V3. | |
| 856 | + */ | |
| 857 | + void tNeuron_setV3 (tNeuron* const neuron, float V3); | |
| 858 | + | |
| 859 | + | |
| 860 | + //! Set the time step. | |
| 861 | + /*! | |
| 862 | + @param neuron A pointer to the relevant tNeuron. | |
| 863 | + @param timestep The new time step | |
| 864 | + */ | |
| 865 | + void tNeuron_setTimeStep (tNeuron* const neuron, float timestep); | |
| 866 | + | |
| 867 | + /*! @} */ | |
| 868 | + | |
| 869 | + //============================================================================== | |
| 870 | + | |
| 871 | + typedef struct _tMinBLEP | |
| 872 | + { | |
| 873 | + float coeffs[6]; | |
| 874 | + | |
| 875 | + // FilterState | |
| 876 | + float* x1, x2, y1, y2; | |
| 877 | + | |
| 878 | + float ratio, lastRatio; | |
| 879 | + | |
| 880 | + float overSamplingRatio; | |
| 881 | + int zeroCrossings; | |
| 882 | + | |
| 883 | + float lastValue; | |
| 884 | + float lastDelta; // previous derivative ... | |
| 885 | + | |
| 886 | + // Tweaking the Blep F | |
| 887 | + float proportionalBlepFreq; | |
| 888 | + uint8_t returnDerivative; // set this to return the FIRST DERIVATIVE of the blep (for first der. discontinuities) | |
| 889 | + | |
| 890 | + int blepIndex; | |
| 891 | + int numActiveBleps; | |
| 892 | + //currentActiveBlepOffsets; | |
| 893 | + float* offset; | |
| 894 | + float* freqMultiple; | |
| 895 | + float* pos_change_magnitude; | |
| 896 | + float* vel_change_magnitude; | |
| 897 | + | |
| 898 | + int minBlepSize; | |
| 899 | + float* minBlepArray; | |
| 900 | + float* minBlepDerivArray; | |
| 901 | + } _tMinBLEP; | |
| 902 | + | |
| 903 | + typedef _tMinBLEP* tMinBLEP; | |
| 904 | + | |
| 905 | + void tMinBLEP_init (tMinBLEP* const minblep, int zeroCrossings, int oversamplerRatio); | |
| 906 | + void tMinBLEP_free (tMinBLEP* const minblep); | |
| 907 | + void tMinBLEP_initToPool (tMinBLEP* const minblep, int zeroCrossings, int oversamplerRatio, tMempool* const pool); | |
| 908 | + void tMinBLEP_freeFromPool (tMinBLEP* const minblep, tMempool* const pool); | |
| 909 | + | |
| 910 | + // pass in audio, identify discontinuities and return the audio with minbleps inserted | |
| 911 | + float tMinBLEP_tick (tMinBLEP* const minblep, float input); | |
| 912 | + void tMinBLEP_buildBLEP (tMinBLEP* const minblep); | |
| 913 | + void tMinBLEP_addBLEP (tMinBLEP* const minblep, float offset, float posChange, float velChange); | |
| 914 | + | |
| 915 | + void tMinBLEP_setOversamplingRatio (tMinBLEP* const minblep, float ratio); | |
| 916 | + void tMinBLEP_setNumZeroCrossings (tMinBLEP* const minblep, int numCrossings); | |
| 917 | + | |
| 918 | + //============================================================================== | |
| 919 | + | |
| 920 | + /* tMBTriangle: Anti-aliased Triangle waveform using wavetable interpolation. */ | |
| 921 | + typedef struct _tMBTriangle | |
| 922 | + { | |
| 923 | + float phase; | |
| 924 | + float inc,freq; | |
| 925 | + float skew; | |
| 926 | + float lastOut; | |
| 927 | + | |
| 928 | + tMinBLEP minBlep; | |
| 929 | + tHighpass dcBlock; | |
| 930 | + } _tMBTriangle; | |
| 931 | + | |
| 932 | + typedef _tMBTriangle* tMBTriangle; | |
| 933 | + | |
| 934 | + /*! | |
| 935 | + * @defgroup tMBTriangle tMBTriangle | |
| 936 | + * @ingroup oscillators | |
| 937 | + * @brief An anti-aliased triangle waveform oscillator. | |
| 938 | + * @{ | |
| 939 | + */ | |
| 940 | + | |
| 941 | + //! Initialize a tMBTriangle to the default LEAF mempool. | |
| 942 | + /*! | |
| 943 | + @param osc A pointer to the tMBTriangle to be initialized. | |
| 944 | + */ | |
| 945 | + void tMBTriangle_init (tMBTriangle* const osc); | |
| 946 | + | |
| 947 | + | |
| 948 | + //! Free a tMBTriangle from the default LEAF mempool. | |
| 949 | + /*! | |
| 950 | + @param osc A pointer to the tMBTriangle to be freed. | |
| 951 | + */ | |
| 952 | + void tMBTriangle_free (tMBTriangle* const osc); | |
| 953 | + | |
| 954 | + | |
| 955 | + //! Initialize a tMBTriangle to a specified mempool. | |
| 956 | + /*! | |
| 957 | + @param osc A pointer to the tMBTriangle to be initialized. | |
| 958 | + @param pool A pointer to the tMempool to which the tMBTriangle should be initialized. | |
| 959 | + */ | |
| 960 | + void tMBTriangle_initToPool (tMBTriangle* const osc, tMempool* const pool); | |
| 961 | + | |
| 962 | + | |
| 963 | + //! Free a tMBTriangle from a specified mempool. | |
| 964 | + /*! | |
| 965 | + @param osc A pointer to the tMBTriangle to be freed. | |
| 966 | + @param pool A pointer to the tMempool from which the tMBTriangle should be freed. | |
| 967 | + */ | |
| 968 | + void tMBTriangle_freeFromPool (tMBTriangle* const osc, tMempool* const pool); | |
| 969 | + | |
| 970 | + | |
| 971 | + //! Tick a tMBTriangle oscillator. | |
| 972 | + /*! | |
| 973 | + @param osc A pointer to the relevant tMBTriangle. | |
| 974 | + @return The ticked sample as a float from -1 to 1. | |
| 975 | + */ | |
| 976 | + float tMBTriangle_tick (tMBTriangle* const osc); | |
| 977 | + | |
| 978 | + | |
| 979 | + //! Set the frequency of a tMBTriangle oscillator. | |
| 980 | + /*! | |
| 981 | + @param osc A pointer to the relevant tMBTriangleangle. | |
| 982 | + @param freq The frequency to set the oscillator to. | |
| 983 | + */ | |
| 984 | + void tMBTriangle_setFreq (tMBTriangle* const osc, float freq); | |
| 985 | + | |
| 986 | + void tMBTriangle_setSkew (tMBTriangle* const osc, float skew); | |
| 987 | + | |
| 988 | + void tMBTriangle_sync (tMBTriangle* const osc, float phase); | |
| 989 | + | |
| 990 | + /*! @} */ | |
| 991 | + | |
| 992 | + //============================================================================== | |
| 993 | + | |
| 994 | + /* tMBPulse: Anti-aliased Square waveform. */ | |
| 995 | + typedef struct _tMBPulse | |
| 996 | + { | |
| 997 | + float phase; | |
| 998 | + float inc,freq; | |
| 999 | + float width; | |
| 1000 | + | |
| 1001 | + tMinBLEP minBlep; | |
| 1002 | + tHighpass dcBlock; | |
| 1003 | + } _tMBPulse; | |
| 1004 | + | |
| 1005 | + typedef _tMBPulse* tMBPulse; | |
| 1006 | + | |
| 1007 | + /*! | |
| 1008 | + * @defgroup tMBPulse tMBPulse | |
| 1009 | + * @ingroup oscillators | |
| 1010 | + * @brief An anti-aliased pulse waveform oscillator with changeable pulse width. | |
| 1011 | + * @{ | |
| 1012 | + */ | |
| 1013 | + | |
| 1014 | + //! Initialize a tMBPulse to the default LEAF mempool. | |
| 1015 | + /*! | |
| 1016 | + @param osc A pointer to the tMBPulse to be initialized. | |
| 1017 | + */ | |
| 1018 | + void tMBPulse_init (tMBPulse* const osc); | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + //! Free a tMBPulse from the default LEAF mempool. | |
| 1022 | + /*! | |
| 1023 | + @param osc A pointer to the tMBPulse to be freed. | |
| 1024 | + */ | |
| 1025 | + void tMBPulse_free (tMBPulse* const osc); | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + //! Initialize a tMBPulse to a specified mempool. | |
| 1029 | + /*! | |
| 1030 | + @param osc A pointer to the tMBPulse to be initialized. | |
| 1031 | + @param pool A pointer to the tMempool to which the tMBPulse should be initialized. | |
| 1032 | + */ | |
| 1033 | + void tMBPulse_initToPool (tMBPulse* const osc, tMempool* const); | |
| 1034 | + | |
| 1035 | + | |
| 1036 | + //! Free a tMBPulse from a specified mempool. | |
| 1037 | + /*! | |
| 1038 | + @param osc A pointer to the tMBPulse to be freed. | |
| 1039 | + @param pool A pointer to the tMempool from which the tMBPulse should be freed. | |
| 1040 | + */ | |
| 1041 | + void tMBPulse_freeFromPool(tMBPulse* const osc, tMempool* const); | |
| 1042 | + | |
| 1043 | + | |
| 1044 | + //! Tick a tMBPulse oscillator. | |
| 1045 | + /*! | |
| 1046 | + @param osc A pointer to the relevant tMBPulse. | |
| 1047 | + @return The ticked sample as a float from -1 to 1. | |
| 1048 | + */ | |
| 1049 | + float tMBPulse_tick (tMBPulse* const osc); | |
| 1050 | + | |
| 1051 | + | |
| 1052 | + //! Set the frequency of a tMBPulse oscillator. | |
| 1053 | + /*! | |
| 1054 | + @param osc A pointer to the relevant tMBPulse. | |
| 1055 | + @param freq The frequency to set the oscillator to. | |
| 1056 | + */ | |
| 1057 | + void tMBPulse_setFreq (tMBPulse* const osc, float freq); | |
| 1058 | + | |
| 1059 | + void tMBPulse_setWidth (tMBPulse* const osc, float width); | |
| 1060 | + | |
| 1061 | + void tMBPulse_sync (tMBPulse* const osc, float phase); | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + /*! @} */ | |
| 1065 | + | |
| 1066 | + //============================================================================== | |
| 1067 | + | |
| 1068 | + /* tMBSawtooth: Anti-aliased Sawtooth waveform. */ | |
| 1069 | + typedef struct _tMBSaw | |
| 1070 | + { | |
| 1071 | + float phase; | |
| 1072 | + float inc,freq; | |
| 1073 | + | |
| 1074 | + tMinBLEP minBlep; | |
| 1075 | + tHighpass dcBlock; | |
| 1076 | + } _tMBSaw; | |
| 1077 | + | |
| 1078 | + typedef _tMBSaw* tMBSaw; | |
| 1079 | + | |
| 1080 | + /*! | |
| 1081 | + * @defgroup tMBSaw tMBSaw | |
| 1082 | + * @ingroup oscillators | |
| 1083 | + * @brief An anti-aliased sawtooth waveform oscillator. Uses wavetable synthesis. | |
| 1084 | + * @{ | |
| 1085 | + */ | |
| 1086 | + | |
| 1087 | + //! Initialize a tMBSaw to the default LEAF mempool. | |
| 1088 | + /*! | |
| 1089 | + @param osc A pointer to the tMBSaw to be initialized. | |
| 1090 | + */ | |
| 1091 | + void tMBSaw_init (tMBSaw* const osc); | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + //! Free a tMBSaw from the default LEAF mempool. | |
| 1095 | + /*! | |
| 1096 | + @param osc A pointer to the tMBSaw to be freed. | |
| 1097 | + */ | |
| 1098 | + void tMBSaw_free (tMBSaw* const osc); | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + //! Initialize a tMBSaw to a specified mempool. | |
| 1102 | + /*! | |
| 1103 | + @param osc A pointer to the tMBSaw to be initialized. | |
| 1104 | + @param pool A pointer to the tMempool to which the tMBSaw should be initialized. | |
| 1105 | + */ | |
| 1106 | + void tMBSaw_initToPool (tMBSaw* const osc, tMempool* const pool); | |
| 1107 | + | |
| 1108 | + | |
| 1109 | + //! Free a tMBSaw from a specified mempool. | |
| 1110 | + /*! | |
| 1111 | + @param osc A pointer to the tMBSaw to be freed. | |
| 1112 | + @param pool A pointer to the tMempool from which the tMBSaw should be freed. | |
| 1113 | + */ | |
| 1114 | + void tMBSaw_freeFromPool (tMBSaw* const osc, tMempool* const pool); | |
| 1115 | + | |
| 1116 | + | |
| 1117 | + //! Tick a tMBSaw oscillator. | |
| 1118 | + /*! | |
| 1119 | + @param osc A pointer to the relevant tMBSaw. | |
| 1120 | + @return The ticked sample as a float from -1 to 1. | |
| 1121 | + */ | |
| 1122 | + float tMBSaw_tick (tMBSaw* const osc); | |
| 1123 | + | |
| 1124 | + | |
| 1125 | + //! Set the frequency of a tMBSaw oscillator. | |
| 1126 | + /*! | |
| 1127 | + @param osc A pointer to the relevant tMBSaw. | |
| 1128 | + @param freq The frequency to set the oscillator to. | |
| 1129 | + */ | |
| 1130 | + void tMBSaw_setFreq (tMBSaw* const osc, float freq); | |
| 1131 | + | |
| 1132 | + void tMBSaw_sync (tMBSaw* const osc, float phase); | |
| 1133 | + | |
| 1134 | + /*! @} */ | |
| 1135 | + | |
| 1136 | +#ifdef __cplusplus | |
| 1137 | +} | |
| 1138 | +#endif | |
| 1139 | + | |
| 1140 | +#endif // LEAF_OSCILLATORS_H_INCLUDED | |
| 1141 | + | |
| 1142 | +//============================================================================== | |
| 1143 | + | |
| @@ -0,0 +1,259 @@ | ||
| 1 | +/* | |
| 2 | + ============================================================================== | |
| 3 | + | |
| 4 | + leaf-physical.h | |
| 5 | + Created: 30 Nov 2018 10:41:55am | |
| 6 | + Author: airship | |
| 7 | + | |
| 8 | + ============================================================================== | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#ifndef LEAF_PHYSICAL_H_INCLUDED | |
| 12 | +#define LEAF_PHYSICAL_H_INCLUDED | |
| 13 | + | |
| 14 | +#ifdef __cplusplus | |
| 15 | +extern "C" { | |
| 16 | +#endif | |
| 17 | + | |
| 18 | + //============================================================================== | |
| 19 | + | |
| 20 | +#include "leaf-global.h" | |
| 21 | +#include "leaf-math.h" | |
| 22 | +#include "leaf-mempool.h" | |
| 23 | +#include "leaf-delay.h" | |
| 24 | +#include "leaf-filters.h" | |
| 25 | +#include "leaf-oscillators.h" | |
| 26 | +#include "leaf-envelopes.h" | |
| 27 | +#include "leaf-dynamics.h" | |
| 28 | + | |
| 29 | + /*! | |
| 30 | + * @internal | |
| 31 | + * Header. | |
| 32 | + * @include basic-oscillators.h | |
| 33 | + * @example basic-oscillators.c | |
| 34 | + * An example. | |
| 35 | + */ | |
| 36 | + | |
| 37 | + //============================================================================== | |
| 38 | + | |
| 39 | + /* Karplus Strong model */ | |
| 40 | + typedef struct _tPluck | |
| 41 | + { | |
| 42 | + tAllpassDelay delayLine; // Allpass or Linear?? big difference... | |
| 43 | + tOneZero loopFilter; | |
| 44 | + tOnePole pickFilter; | |
| 45 | + tNoise noise; | |
| 46 | + | |
| 47 | + float lastOut; | |
| 48 | + float loopGain; | |
| 49 | + float lastFreq; | |
| 50 | + | |
| 51 | + float sr; | |
| 52 | + | |
| 53 | + } _tPluck; | |
| 54 | + | |
| 55 | + typedef _tPluck* tPluck; | |
| 56 | + | |
| 57 | + void tPluck_init (tPluck* const, float lowestFrequency); //float delayBuff[DELAY_LENGTH]); | |
| 58 | + void tPluck_free (tPluck* const); | |
| 59 | + void tPluck_initToPool (tPluck* const, float lowestFrequency, tMempool* const); | |
| 60 | + void tPluck_freeFromPool (tPluck* const, tMempool* const); | |
| 61 | + | |
| 62 | + float tPluck_tick (tPluck* const); | |
| 63 | + | |
| 64 | + // Pluck the string. | |
| 65 | + void tPluck_pluck (tPluck* const, float amplitude); | |
| 66 | + | |
| 67 | + // Start a note with the given frequency and amplitude.; | |
| 68 | + void tPluck_noteOn (tPluck* const, float frequency, float amplitude ); | |
| 69 | + | |
| 70 | + // Stop a note with the given amplitude (speed of decay). | |
| 71 | + void tPluck_noteOff (tPluck* const, float amplitude ); | |
| 72 | + | |
| 73 | + // Set instrument parameters for a particular frequency. | |
| 74 | + void tPluck_setFrequency (tPluck* const, float frequency ); | |
| 75 | + | |
| 76 | + // Perform the control change specified by \e number and \e value (0.0 - 128.0). | |
| 77 | + void tPluck_controlChange (tPluck* const, int number, float value); | |
| 78 | + | |
| 79 | + // tPluck Utilities. | |
| 80 | + float tPluck_getLastOut (tPluck* const); | |
| 81 | + | |
| 82 | + //============================================================================== | |
| 83 | + | |
| 84 | + typedef enum SKControlType | |
| 85 | + { | |
| 86 | + SKPickPosition = 0, | |
| 87 | + SKStringDamping, | |
| 88 | + SKDetune, | |
| 89 | + SKControlTypeNil | |
| 90 | + } SKControlType; | |
| 91 | + | |
| 92 | + /* Stif Karplus Strong model */ | |
| 93 | + typedef struct _tKarplusStrong | |
| 94 | + { | |
| 95 | + tAllpassDelay delayLine; | |
| 96 | + tLinearDelay combDelay; | |
| 97 | + tOneZero filter; | |
| 98 | + tNoise noise; | |
| 99 | + tBiQuad biquad[4]; | |
| 100 | + | |
| 101 | + uint32_t length; | |
| 102 | + float loopGain; | |
| 103 | + float baseLoopGain; | |
| 104 | + float lastFrequency; | |
| 105 | + float lastLength; | |
| 106 | + float stretching; | |
| 107 | + float pluckAmplitude; | |
| 108 | + float pickupPosition; | |
| 109 | + | |
| 110 | + float lastOut; | |
| 111 | + | |
| 112 | + } _tKarplusStrong; | |
| 113 | + | |
| 114 | + typedef _tKarplusStrong* tKarplusStrong; | |
| 115 | + | |
| 116 | + void tKarplusStrong_init (tKarplusStrong* const, float lowestFrequency); // float delayBuff[2][DELAY_LENGTH]); | |
| 117 | + void tKarplusStrong_free (tKarplusStrong* const); | |
| 118 | + void tKarplusStrong_initToPool (tKarplusStrong* const, float lowestFrequency, tMempool* const); | |
| 119 | + void tKarplusStrong_freeFromPool (tKarplusStrong* const, tMempool* const); | |
| 120 | + | |
| 121 | + float tKarplusStrong_tick (tKarplusStrong* const); | |
| 122 | + | |
| 123 | + // Pluck the string. | |
| 124 | + void tKarplusStrong_pluck (tKarplusStrong* const, float amplitude); | |
| 125 | + | |
| 126 | + // Start a note with the given frequency and amplitude.; | |
| 127 | + void tKarplusStrong_noteOn (tKarplusStrong* const, float frequency, float amplitude ); | |
| 128 | + | |
| 129 | + // Stop a note with the given amplitude (speed of decay). | |
| 130 | + void tKarplusStrong_noteOff (tKarplusStrong* const, float amplitude ); | |
| 131 | + | |
| 132 | + // Set instrument parameters for a particular frequency. | |
| 133 | + void tKarplusStrong_setFrequency (tKarplusStrong* const, float frequency ); | |
| 134 | + | |
| 135 | + // Perform the control change specified by \e number and \e value (0.0 - 128.0). | |
| 136 | + // Use SKPickPosition, SKStringDamping, or SKDetune for type. | |
| 137 | + void tKarplusStrong_controlChange (tKarplusStrong* const, SKControlType type, float value); | |
| 138 | + | |
| 139 | + // Set the stretch "factor" of the string (0.0 - 1.0). | |
| 140 | + void tKarplusStrong_setStretch (tKarplusStrong* const, float stretch ); | |
| 141 | + | |
| 142 | + // Set the pluck or "excitation" position along the string (0.0 - 1.0). | |
| 143 | + void tKarplusStrong_setPickupPosition (tKarplusStrong* const, float position ); | |
| 144 | + | |
| 145 | + // Set the base loop gain. | |
| 146 | + void tKarplusStrong_setBaseLoopGain (tKarplusStrong* const, float aGain ); | |
| 147 | + | |
| 148 | + // tKarplusStrong utilities. | |
| 149 | + float tKarplusStrong_getLastOut (tKarplusStrong* const); | |
| 150 | + | |
| 151 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 152 | + | |
| 153 | + /* Simple Living String */ | |
| 154 | + typedef struct _tSimpleLivingString { | |
| 155 | + float freq, waveLengthInSamples; // the frequency of the string, determining delay length | |
| 156 | + float dampFreq; // frequency for the bridge LP filter, in Hz | |
| 157 | + float decay; // amplitude damping factor for the string (only active in mode 0) | |
| 158 | + float levMode; | |
| 159 | + float curr; | |
| 160 | + tLinearDelay delayLine; | |
| 161 | + tOnePole bridgeFilter; | |
| 162 | + tHighpass DCblocker; | |
| 163 | + tFeedbackLeveler fbLev; | |
| 164 | + tExpSmooth wlSmooth; | |
| 165 | + } _tSimpleLivingString; | |
| 166 | + | |
| 167 | + typedef _tSimpleLivingString* tSimpleLivingString; | |
| 168 | + | |
| 169 | + void tSimpleLivingString_init (tSimpleLivingString* const, float freq, float dampFreq, | |
| 170 | + float decay, float targetLev, float levSmoothFactor, | |
| 171 | + float levStrength, int levMode); | |
| 172 | + void tSimpleLivingString_free (tSimpleLivingString* const); | |
| 173 | + void tSimpleLivingString_initToPool (tSimpleLivingString* const, float freq, float dampFreq, | |
| 174 | + float decay, float targetLev, float levSmoothFactor, | |
| 175 | + float levStrength, int levMode, tMempool* const); | |
| 176 | + void tSimpleLivingString_freeFromPool (tSimpleLivingString* const, tMempool* const); | |
| 177 | + | |
| 178 | + float tSimpleLivingString_tick (tSimpleLivingString* const, float input); | |
| 179 | + float tSimpleLivingString_sample (tSimpleLivingString* const); | |
| 180 | + void tSimpleLivingString_setFreq (tSimpleLivingString* const, float freq); | |
| 181 | + void tSimpleLivingString_setWaveLength (tSimpleLivingString* const, float waveLength); // in samples | |
| 182 | + void tSimpleLivingString_setDampFreq (tSimpleLivingString* const, float dampFreq); | |
| 183 | + void tSimpleLivingString_setDecay (tSimpleLivingString* const, float decay); // should be near 1.0 | |
| 184 | + void tSimpleLivingString_setTargetLev (tSimpleLivingString* const, float targetLev); | |
| 185 | + void tSimpleLivingString_setLevSmoothFactor (tSimpleLivingString* const, float levSmoothFactor); | |
| 186 | + void tSimpleLivingString_setLevStrength (tSimpleLivingString* const, float levStrength); | |
| 187 | + void tSimpleLivingString_setLevMode (tSimpleLivingString* const, int levMode); | |
| 188 | + | |
| 189 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 190 | + | |
| 191 | + /* Living String */ | |
| 192 | + typedef struct _tLivingString { | |
| 193 | + float freq, waveLengthInSamples; // the frequency of the whole string, determining delay length | |
| 194 | + float pickPos; // the pick position, dividing the string in two, in ratio | |
| 195 | + float prepIndex; // the amount of pressure on the pickpoint of the string (near 0=soft obj, near 1=hard obj) | |
| 196 | + float dampFreq; // frequency for the bridge LP filter, in Hz | |
| 197 | + float decay; // amplitude damping factor for the string (only active in mode 0) | |
| 198 | + float levMode; | |
| 199 | + float curr; | |
| 200 | + tLinearDelay delLF,delUF,delUB,delLB; // delay for lower/upper/forward/backward part of the waveguide model | |
| 201 | + tOnePole bridgeFilter, nutFilter, prepFilterU, prepFilterL; | |
| 202 | + tHighpass DCblockerL, DCblockerU; | |
| 203 | + tFeedbackLeveler fbLevU, fbLevL; | |
| 204 | + tExpSmooth wlSmooth, ppSmooth; | |
| 205 | + } _tLivingString; | |
| 206 | + | |
| 207 | + typedef _tLivingString* tLivingString; | |
| 208 | + | |
| 209 | + void tLivingString_init (tLivingString* const, float freq, float pickPos, float prepIndex, | |
| 210 | + float dampFreq, float decay, float targetLev, float levSmoothFactor, | |
| 211 | + float levStrength, int levMode); | |
| 212 | + void tLivingString_free (tLivingString* const); | |
| 213 | + void tLivingString_initToPool (tLivingString* const, float freq, float pickPos, float prepIndex, | |
| 214 | + float dampFreq, float decay, float targetLev, float levSmoothFactor, | |
| 215 | + float levStrength, int levMode, tMempool* const); | |
| 216 | + void tLivingString_freeFromPool (tLivingString* const, tMempool* const); | |
| 217 | + | |
| 218 | + float tLivingString_tick (tLivingString* const, float input); | |
| 219 | + float tLivingString_sample (tLivingString* const); | |
| 220 | + void tLivingString_setFreq (tLivingString* const, float freq); | |
| 221 | + void tLivingString_setWaveLength (tLivingString* const, float waveLength); // in samples | |
| 222 | + void tLivingString_setPickPos (tLivingString* const, float pickPos); | |
| 223 | + void tLivingString_setPrepIndex (tLivingString* const, float prepIndex); | |
| 224 | + void tLivingString_setDampFreq (tLivingString* const, float dampFreq); | |
| 225 | + void tLivingString_setDecay (tLivingString* const, float decay); // should be near 1.0 | |
| 226 | + void tLivingString_setTargetLev (tLivingString* const, float targetLev); | |
| 227 | + void tLivingString_setLevSmoothFactor (tLivingString* const, float levSmoothFactor); | |
| 228 | + void tLivingString_setLevStrength (tLivingString* const, float levStrength); | |
| 229 | + void tLivingString_setLevMode (tLivingString* const, int levMode); | |
| 230 | + | |
| 231 | + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | |
| 232 | + | |
| 233 | + //Reed Table - borrowed from STK | |
| 234 | + typedef struct _tReedTable { | |
| 235 | + float offset, slope; | |
| 236 | + } _tReedTable; | |
| 237 | + | |
| 238 | + typedef _tReedTable* tReedTable; | |
| 239 | + | |
| 240 | + void tReedTable_init (tReedTable* const, float offset, float slope); | |
| 241 | + void tReedTable_free (tReedTable* const); | |
| 242 | + void tReedTable_initToPool (tReedTable* const, float offset, float slope, tMempool* const); | |
| 243 | + void tReedTable_freeFromPool (tReedTable* const, tMempool* const); | |
| 244 | + | |
| 245 | + float tReedTable_tick (tReedTable* const, float input); | |
| 246 | + float tReedTable_tanh_tick (tReedTable* const, float input); //tanh softclip version of reed table - replacing the hard clip in original stk code | |
| 247 | + void tReedTable_setOffset (tReedTable* const, float offset); | |
| 248 | + void tReedTable_setSlope (tReedTable* const, float slope); | |
| 249 | + | |
| 250 | + //============================================================================== | |
| 251 | + | |
| 252 | +#ifdef __cplusplus | |
| 253 | +} | |
| 254 | +#endif | |
| 255 | + | |
| 256 | +#endif // LEAF_PHYSICAL_H_INCLUDED | |
| 257 | + | |
| 258 | +//============================================================================== | |
| 259 | + | |
| @@ -0,0 +1,175 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-reverb.h | |
| 4 | + Created: 20 Jan 2017 12:02:04pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_REVERB_H_INCLUDED | |
| 10 | +#define LEAF_REVERB_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | + //============================================================================== | |
| 17 | + | |
| 18 | +#include "leaf-global.h" | |
| 19 | +#include "leaf-math.h" | |
| 20 | +#include "leaf-mempool.h" | |
| 21 | +#include "leaf-delay.h" | |
| 22 | +#include "leaf-filters.h" | |
| 23 | +#include "leaf-oscillators.h" | |
| 24 | + | |
| 25 | + /*! | |
| 26 | + * @internal | |
| 27 | + * Header. | |
| 28 | + * @include basic-oscillators.h | |
| 29 | + * @example basic-oscillators.c | |
| 30 | + * An example. | |
| 31 | + */ | |
| 32 | + | |
| 33 | + //============================================================================== | |
| 34 | + | |
| 35 | + /* PRCReverb: Reverb, reimplemented from STK (Cook and Scavone). */ | |
| 36 | + typedef struct _tPRCReverb | |
| 37 | + { | |
| 38 | + float mix, t60; | |
| 39 | + | |
| 40 | + float inv_441; | |
| 41 | + | |
| 42 | + tDelay allpassDelays[2]; | |
| 43 | + tDelay combDelay; | |
| 44 | + float allpassCoeff; | |
| 45 | + float combCoeff; | |
| 46 | + | |
| 47 | + float lastIn, lastOut; | |
| 48 | + | |
| 49 | + } _tPRCReverb; | |
| 50 | + | |
| 51 | + typedef _tPRCReverb* tPRCReverb; | |
| 52 | + | |
| 53 | + void tPRCReverb_init (tPRCReverb* const, float t60); | |
| 54 | + void tPRCReverb_free (tPRCReverb* const); | |
| 55 | + void tPRCReverb_initToPool (tPRCReverb* const, float t60, tMempool* const); | |
| 56 | + void tPRCReverb_freeFromPool (tPRCReverb* const, tMempool* const); | |
| 57 | + | |
| 58 | + void tPRCReverb_clear (tPRCReverb* const); | |
| 59 | + float tPRCReverb_tick (tPRCReverb* const, float input); | |
| 60 | + | |
| 61 | + // Set reverb time in seconds. | |
| 62 | + void tPRCReverb_setT60 (tPRCReverb* const, float t60); | |
| 63 | + | |
| 64 | + // Set mix between dry input and wet output signal. | |
| 65 | + void tPRCReverb_setMix (tPRCReverb* const, float mix); | |
| 66 | + | |
| 67 | + //============================================================================== | |
| 68 | + | |
| 69 | + /* NReverb: Reverb, reimplemented from STK (Cook and Scavone). */ | |
| 70 | + typedef struct _tNReverb | |
| 71 | + { | |
| 72 | + float mix, t60; | |
| 73 | + | |
| 74 | + float inv_sr, inv_441; | |
| 75 | + | |
| 76 | + tLinearDelay allpassDelays[8]; | |
| 77 | + tLinearDelay combDelays[6]; | |
| 78 | + float allpassCoeff; | |
| 79 | + float combCoeffs[6]; | |
| 80 | + float lowpassState; | |
| 81 | + | |
| 82 | + float lastIn, lastOut; | |
| 83 | + | |
| 84 | + } _tNReverb; | |
| 85 | + | |
| 86 | + typedef _tNReverb* tNReverb; | |
| 87 | + | |
| 88 | + void tNReverb_init (tNReverb* const, float t60); | |
| 89 | + void tNReverb_free (tNReverb* const); | |
| 90 | + void tNReverb_initToPool (tNReverb* const, float t60, tMempool* const); | |
| 91 | + void tNReverb_freeFromPool (tNReverb* const, tMempool* const); | |
| 92 | + | |
| 93 | + void tNReverb_clear (tNReverb* const); | |
| 94 | + float tNReverb_tick (tNReverb* const, float input); | |
| 95 | + void tNReverb_tickStereo (tNReverb* const rev, float input, float* output); | |
| 96 | + | |
| 97 | + // Set reverb time in seconds. | |
| 98 | + void tNReverb_setT60 (tNReverb* const, float t60); | |
| 99 | + | |
| 100 | + // Set mix between dry input and wet output signal. | |
| 101 | + void tNReverb_setMix (tNReverb* const, float mix); | |
| 102 | + | |
| 103 | + //============================================================================== | |
| 104 | + | |
| 105 | + typedef struct _tDattorroReverb | |
| 106 | + { | |
| 107 | + float predelay; | |
| 108 | + float input_filter; | |
| 109 | + float feedback_filter; | |
| 110 | + float feedback_gain; | |
| 111 | + float mix; | |
| 112 | + uint32_t frozen; | |
| 113 | + | |
| 114 | + float size, size_max, t; | |
| 115 | + | |
| 116 | + float f1_delay_2_last, | |
| 117 | + f2_delay_2_last; | |
| 118 | + | |
| 119 | + float f1_last, | |
| 120 | + f2_last; | |
| 121 | + | |
| 122 | + // INPUT | |
| 123 | + tTapeDelay in_delay; | |
| 124 | + tOnePole in_filter; | |
| 125 | + tAllpass in_allpass[4]; | |
| 126 | + | |
| 127 | + // FEEDBACK 1 | |
| 128 | + tAllpass f1_allpass; | |
| 129 | + tTapeDelay f1_delay_1; | |
| 130 | + tOnePole f1_filter; | |
| 131 | + tTapeDelay f1_delay_2; | |
| 132 | + tTapeDelay f1_delay_3; | |
| 133 | + tHighpass f1_hp; | |
| 134 | + | |
| 135 | + tCycle f1_lfo; | |
| 136 | + | |
| 137 | + // FEEDBACK 2 | |
| 138 | + tAllpass f2_allpass; | |
| 139 | + tTapeDelay f2_delay_1; | |
| 140 | + tOnePole f2_filter; | |
| 141 | + tTapeDelay f2_delay_2; | |
| 142 | + tTapeDelay f2_delay_3; | |
| 143 | + tHighpass f2_hp; | |
| 144 | + | |
| 145 | + tCycle f2_lfo; | |
| 146 | + | |
| 147 | + } _tDattorroReverb; | |
| 148 | + | |
| 149 | + typedef _tDattorroReverb* tDattorroReverb; | |
| 150 | + | |
| 151 | + void tDattorroReverb_init (tDattorroReverb* const); | |
| 152 | + void tDattorroReverb_free (tDattorroReverb* const); | |
| 153 | + void tDattorroReverb_initToPool (tDattorroReverb* const, tMempool* const); | |
| 154 | + void tDattorroReverb_freeFromPool (tDattorroReverb* const, tMempool* const); | |
| 155 | + | |
| 156 | + void tDattorroReverb_clear (tDattorroReverb* const); | |
| 157 | + float tDattorroReverb_tick (tDattorroReverb* const, float input); | |
| 158 | + void tDattorroReverb_tickStereo (tDattorroReverb* const rev, float input, float* output); | |
| 159 | + void tDattorroReverb_setMix (tDattorroReverb* const, float mix); | |
| 160 | + void tDattorroReverb_setFreeze (tDattorroReverb* const rev, uint32_t freeze); | |
| 161 | + void tDattorroReverb_setHP (tDattorroReverb* const, float freq); | |
| 162 | + void tDattorroReverb_setSize (tDattorroReverb* const, float size); | |
| 163 | + void tDattorroReverb_setInputDelay (tDattorroReverb* const, float preDelay); | |
| 164 | + void tDattorroReverb_setInputFilter (tDattorroReverb* const, float freq); | |
| 165 | + void tDattorroReverb_setFeedbackFilter (tDattorroReverb* const, float freq); | |
| 166 | + void tDattorroReverb_setFeedbackGain (tDattorroReverb* const, float gain); | |
| 167 | + | |
| 168 | +#ifdef __cplusplus | |
| 169 | +} | |
| 170 | +#endif | |
| 171 | + | |
| 172 | +#endif // LEAF_REVERB_H_INCLUDED | |
| 173 | + | |
| 174 | +//============================================================================== | |
| 175 | + | |
| @@ -0,0 +1,188 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-sampling.h | |
| 4 | + Created: 23 Jan 2019 11:22:09am | |
| 5 | + Author: Mike Mulshine | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | + | |
| 10 | +#ifndef LEAF_SAMPLING_H_INCLUDED | |
| 11 | +#define LEAF_SAMPLING_H_INCLUDED | |
| 12 | + | |
| 13 | +#ifdef __cplusplus | |
| 14 | +extern "C" { | |
| 15 | +#endif | |
| 16 | + | |
| 17 | + //============================================================================== | |
| 18 | + | |
| 19 | +#include "leaf-global.h" | |
| 20 | +#include "leaf-math.h" | |
| 21 | +#include "leaf-mempool.h" | |
| 22 | +#include "leaf-envelopes.h" | |
| 23 | +#include "leaf-mempool.h" | |
| 24 | +#include "leaf-analysis.h" | |
| 25 | + | |
| 26 | + /*! | |
| 27 | + * @internal | |
| 28 | + * Header. | |
| 29 | + * @include basic-oscillators.h | |
| 30 | + * @example basic-oscillators.c | |
| 31 | + * An example. | |
| 32 | + */ | |
| 33 | + | |
| 34 | + //============================================================================== | |
| 35 | + | |
| 36 | + typedef enum RecordMode | |
| 37 | + { | |
| 38 | + RecordOneShot = 0, | |
| 39 | + RecordLoop, | |
| 40 | + RecordModeNil | |
| 41 | + } RecordMode; | |
| 42 | + | |
| 43 | + typedef struct _tBuffer | |
| 44 | + { | |
| 45 | + float* buff; | |
| 46 | + | |
| 47 | + uint32_t idx; | |
| 48 | + uint32_t bufferLength; | |
| 49 | + uint32_t recordedLength; | |
| 50 | + RecordMode mode; | |
| 51 | + | |
| 52 | + int active; | |
| 53 | + | |
| 54 | + } _tBuffer; | |
| 55 | + | |
| 56 | + typedef _tBuffer* tBuffer; | |
| 57 | + | |
| 58 | + void tBuffer_init (tBuffer* const, uint32_t length); | |
| 59 | + void tBuffer_free (tBuffer* const); | |
| 60 | + void tBuffer_initToPool (tBuffer* const, uint32_t length, tMempool* const); | |
| 61 | + void tBuffer_freeFromPool (tBuffer* const, tMempool* const); | |
| 62 | + | |
| 63 | + void tBuffer_tick (tBuffer* const, float sample); | |
| 64 | + | |
| 65 | + void tBuffer_read (tBuffer* const, float* buff, uint32_t len); | |
| 66 | + | |
| 67 | + float tBuffer_get (tBuffer* const, int idx); | |
| 68 | + | |
| 69 | + void tBuffer_record (tBuffer* const); | |
| 70 | + void tBuffer_stop (tBuffer* const); | |
| 71 | + int tBuffer_getRecordPosition (tBuffer* const); | |
| 72 | + | |
| 73 | + void tBuffer_setRecordMode (tBuffer* const, RecordMode mode); | |
| 74 | + | |
| 75 | + void tBuffer_clear (tBuffer* const); | |
| 76 | + | |
| 77 | + uint32_t tBuffer_getBufferLength (tBuffer* const); | |
| 78 | + uint32_t tBuffer_getRecordedLength (tBuffer* const sb); | |
| 79 | + | |
| 80 | + //============================================================================== | |
| 81 | + | |
| 82 | + typedef enum PlayMode | |
| 83 | + { | |
| 84 | + PlayNormal, | |
| 85 | + PlayLoop, | |
| 86 | + PlayBackAndForth, | |
| 87 | + PlayModeNil | |
| 88 | + } PlayMode; | |
| 89 | + | |
| 90 | + typedef struct _tSampler | |
| 91 | + { | |
| 92 | + tBuffer samp; | |
| 93 | + | |
| 94 | + tRamp gain; | |
| 95 | + | |
| 96 | + float idx; | |
| 97 | + float inc; | |
| 98 | + float last; | |
| 99 | + float iinc; | |
| 100 | + int8_t dir; | |
| 101 | + int8_t flip; | |
| 102 | + int8_t bnf; | |
| 103 | + | |
| 104 | + int32_t start, targetstart; | |
| 105 | + int32_t end, targetend; | |
| 106 | + | |
| 107 | + uint32_t len; | |
| 108 | + uint32_t cfxlen; | |
| 109 | + float numticks; | |
| 110 | + PlayMode mode; | |
| 111 | + int retrigger; | |
| 112 | + | |
| 113 | + int active; | |
| 114 | + | |
| 115 | + uint8_t inCrossfade; | |
| 116 | + | |
| 117 | + float flipStart; | |
| 118 | + float flipIdx; | |
| 119 | + | |
| 120 | + } _tSampler; | |
| 121 | + | |
| 122 | + typedef _tSampler* tSampler; | |
| 123 | + | |
| 124 | + void tSampler_init (tSampler* const, tBuffer* const); | |
| 125 | + void tSampler_free (tSampler* const); | |
| 126 | + void tSampler_initToPool (tSampler* const, tBuffer* const, tMempool* const); | |
| 127 | + void tSampler_freeFromPool (tSampler* const, tMempool* const); | |
| 128 | + | |
| 129 | + float tSampler_tick (tSampler* const); | |
| 130 | + | |
| 131 | + void tSampler_setSample (tSampler* const, tBuffer* const); | |
| 132 | + | |
| 133 | + void tSampler_setMode (tSampler* const, PlayMode mode); | |
| 134 | + | |
| 135 | + void tSampler_play (tSampler* const); | |
| 136 | + void tSampler_stop (tSampler* const); | |
| 137 | + | |
| 138 | + void tSampler_setStart (tSampler* const, int32_t start); | |
| 139 | + void tSampler_setEnd (tSampler* const, int32_t end); | |
| 140 | + void tSampler_setLength (tSampler* const, int32_t length); | |
| 141 | + | |
| 142 | + void tSampler_setCrossfadeLength (tSampler* const sp, uint32_t length); | |
| 143 | + | |
| 144 | + void tSampler_setRate (tSampler* const, float rate); | |
| 145 | + | |
| 146 | + //============================================================================== | |
| 147 | + | |
| 148 | + typedef struct _tAutoSampler | |
| 149 | + { | |
| 150 | + tSampler sampler; | |
| 151 | + tEnvelopeFollower ef; | |
| 152 | + uint32_t windowSize; | |
| 153 | + float threshold; | |
| 154 | + float previousPower; | |
| 155 | + uint32_t sampleCounter; | |
| 156 | + uint32_t powerCounter; | |
| 157 | + uint8_t sampleTriggered; | |
| 158 | + } _tAutoSampler; | |
| 159 | + | |
| 160 | + typedef _tAutoSampler* tAutoSampler; | |
| 161 | + | |
| 162 | + void tAutoSampler_init (tAutoSampler* const, tBuffer* const); | |
| 163 | + void tAutoSampler_free (tAutoSampler* const); | |
| 164 | + void tAutoSampler_initToPool (tAutoSampler* const, tBuffer* const, tMempool* const); | |
| 165 | + void tAutoSampler_freeFromPool (tAutoSampler* const, tMempool* const); | |
| 166 | + | |
| 167 | + float tAutoSampler_tick (tAutoSampler* const, float input); | |
| 168 | + | |
| 169 | + void tAutoSampler_setBuffer (tAutoSampler* const, tBuffer* const); | |
| 170 | + | |
| 171 | + void tAutoSampler_setMode (tAutoSampler* const, PlayMode mode); | |
| 172 | + | |
| 173 | + void tAutoSampler_play (tAutoSampler* const); | |
| 174 | + void tAutoSampler_stop (tAutoSampler* const); | |
| 175 | + | |
| 176 | + void tAutoSampler_setThreshold (tAutoSampler* const, float thresh); | |
| 177 | + void tAutoSampler_setWindowSize (tAutoSampler* const, uint32_t size); | |
| 178 | + void tAutoSampler_setCrossfadeLength (tAutoSampler* const, uint32_t length); | |
| 179 | + | |
| 180 | + void tAutoSampler_setRate (tAutoSampler* const, float rate); | |
| 181 | + | |
| 182 | +#ifdef __cplusplus | |
| 183 | +} | |
| 184 | +#endif | |
| 185 | + | |
| 186 | +#endif // LEAF_SAMPLING_H_INCLUDED | |
| 187 | + | |
| 188 | +//============================================================================== | |
| @@ -0,0 +1,104 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-tables.h | |
| 4 | + Created: 4 Dec 2016 9:42:41pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#ifndef LEAF_TABLES_H_INCLUDED | |
| 10 | +#define LEAF_TABLES_H_INCLUDED | |
| 11 | + | |
| 12 | +#ifdef __cplusplus | |
| 13 | +extern "C" { | |
| 14 | +#endif | |
| 15 | + | |
| 16 | +//============================================================================== | |
| 17 | + | |
| 18 | +#include "leaf-math.h" | |
| 19 | + | |
| 20 | +//============================================================================== | |
| 21 | + | |
| 22 | +#define USE_SAWTOOTH_TABLE 1 | |
| 23 | +#define USE_SQUARE_TABLE 1 | |
| 24 | +#define USE_TRIANGLE_TABLE 1 | |
| 25 | +#define USE_SHAPER_TABLE 1 | |
| 26 | + | |
| 27 | +#define SINE_TABLE_SIZE 2048 | |
| 28 | +#define SAW_TABLE_SIZE 2048 | |
| 29 | +#define SQR_TABLE_SIZE 2048 | |
| 30 | +#define TRI_TABLE_SIZE 2048 | |
| 31 | +#define EXP_DECAY_TABLE_SIZE 65536 | |
| 32 | +#define ATTACK_DECAY_INC_TABLE_SIZE 65536 | |
| 33 | +#define TANH1_TABLE_SIZE 65536 | |
| 34 | +#define DECAY_COEFF_TABLE_SIZE 4096 | |
| 35 | +#define MTOF1_TABLE_SIZE 4096 | |
| 36 | +#define FILTERTAN_TABLE_SIZE 4096 | |
| 37 | + | |
| 38 | +#define SHAPER1_TABLE_SIZE 65536 | |
| 39 | +extern const float __leaf_table_shaper1[SHAPER1_TABLE_SIZE]; | |
| 40 | + | |
| 41 | +#define COEFFS_SIZE 32 | |
| 42 | +extern const float* __leaf_tableref_firCoeffs[COEFFS_SIZE]; | |
| 43 | +extern const float __leaf_tablesize_firNumTaps[COEFFS_SIZE]; | |
| 44 | +extern const float __leaf_table_fir2XLow[32]; | |
| 45 | +extern const float __leaf_table_fir4XLow[64]; | |
| 46 | +extern const float __leaf_table_fir8XLow[64]; | |
| 47 | +extern const float __leaf_table_fir16XLow[128]; | |
| 48 | +extern const float __leaf_table_fir32XLow[256]; | |
| 49 | +extern const float __leaf_table_fir64XLow[256]; | |
| 50 | +extern const float __leaf_table_fir2XHigh[128]; | |
| 51 | +extern const float __leaf_table_fir4XHigh[256]; | |
| 52 | +extern const float __leaf_table_fir8XHigh[256]; | |
| 53 | +extern const float __leaf_table_fir16XHigh[512]; | |
| 54 | +extern const float __leaf_table_fir32XHigh[512]; | |
| 55 | +extern const float __leaf_table_fir64XHigh[1024]; | |
| 56 | + | |
| 57 | +typedef enum TableName | |
| 58 | +{ | |
| 59 | + T20 = 0, | |
| 60 | + T40, | |
| 61 | + T80, | |
| 62 | + T160, | |
| 63 | + T320, | |
| 64 | + T640, | |
| 65 | + T1280, | |
| 66 | + T2560, | |
| 67 | + T5120, | |
| 68 | + T10240, | |
| 69 | + T20480, | |
| 70 | + TableNameNil | |
| 71 | +} TableName; | |
| 72 | + | |
| 73 | +// mtof lookup table based on input range [0.0,1.0) in 4096 increments - midi frequency values scaled between m25 and m134 (from the Snyderphonics DrumBox code) | |
| 74 | + | |
| 75 | +extern const float __leaf_table_exp_decay[EXP_DECAY_TABLE_SIZE]; | |
| 76 | +extern const float __leaf_table_attack_decay_inc[ATTACK_DECAY_INC_TABLE_SIZE]; | |
| 77 | + | |
| 78 | +extern const float __leaf_table_filtertan[FILTERTAN_TABLE_SIZE]; | |
| 79 | + | |
| 80 | +extern const float __leaf_table_mtof1[MTOF1_TABLE_SIZE]; | |
| 81 | +extern const float __leaf_table_decayCoeffTable[DECAY_COEFF_TABLE_SIZE]; | |
| 82 | + | |
| 83 | +extern const float __leaf_table_tanh1[TANH1_TABLE_SIZE]; | |
| 84 | + | |
| 85 | +//============================================================================== | |
| 86 | + | |
| 87 | +/* Sine wave table ripped from http://aquaticus.info/pwm-sine-wave. */ | |
| 88 | +extern const float __leaf_table_sinewave[SINE_TABLE_SIZE]; | |
| 89 | + | |
| 90 | +extern const float __leaf_table_sawtooth[11][SAW_TABLE_SIZE]; | |
| 91 | + | |
| 92 | +extern const float __leaf_table_triangle[11][TRI_TABLE_SIZE]; | |
| 93 | + | |
| 94 | +extern const float __leaf_table_squarewave[11][SQR_TABLE_SIZE]; | |
| 95 | + | |
| 96 | +//============================================================================== | |
| 97 | + | |
| 98 | +#ifdef __cplusplus | |
| 99 | +} | |
| 100 | +#endif | |
| 101 | + | |
| 102 | +#endif // LEAF_TABLES_H_INCLUDED | |
| 103 | + | |
| 104 | +//============================================================================== | |
| @@ -0,0 +1,1054 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-analysis.c | |
| 4 | + Created: 30 Nov 2018 11:56:49am | |
| 5 | + Author: airship | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-analysis.h" | |
| 12 | +#include "..\Externals\d_fft_mayer.h" | |
| 13 | + | |
| 14 | +#else | |
| 15 | + | |
| 16 | +#include "../Inc/leaf-analysis.h" | |
| 17 | +#include "../Externals/d_fft_mayer.h" | |
| 18 | + | |
| 19 | +#endif | |
| 20 | + | |
| 21 | +//=========================================================================== | |
| 22 | +/* Envelope Follower */ | |
| 23 | +//=========================================================================== | |
| 24 | + | |
| 25 | +void tEnvelopeFollower_init(tEnvelopeFollower* const ef, float attackThreshold, float decayCoeff) | |
| 26 | +{ | |
| 27 | + _tEnvelopeFollower* e = *ef = (_tEnvelopeFollower*) leaf_alloc(sizeof(_tEnvelopeFollower)); | |
| 28 | + | |
| 29 | + e->y = 0.0f; | |
| 30 | + e->a_thresh = attackThreshold; | |
| 31 | + e->d_coeff = decayCoeff; | |
| 32 | +} | |
| 33 | + | |
| 34 | +void tEnvelopeFollower_free(tEnvelopeFollower* const ef) | |
| 35 | +{ | |
| 36 | + _tEnvelopeFollower* e = *ef; | |
| 37 | + | |
| 38 | + leaf_free((char*)e); | |
| 39 | +} | |
| 40 | + | |
| 41 | +void tEnvelopeFollower_initToPool (tEnvelopeFollower* const ef, float attackThreshold, float decayCoeff, tMempool* const mp) | |
| 42 | +{ | |
| 43 | + _tMempool* m = *mp; | |
| 44 | + _tEnvelopeFollower* e = *ef = (_tEnvelopeFollower*) mpool_alloc(sizeof(_tEnvelopeFollower), m); | |
| 45 | + | |
| 46 | + e->y = 0.0f; | |
| 47 | + e->a_thresh = attackThreshold; | |
| 48 | + e->d_coeff = decayCoeff; | |
| 49 | +} | |
| 50 | + | |
| 51 | +void tEnvelopeFollower_freeFromPool (tEnvelopeFollower* const ef, tMempool* const mp) | |
| 52 | +{ | |
| 53 | + _tMempool* m = *mp; | |
| 54 | + _tEnvelopeFollower* e = *ef; | |
| 55 | + | |
| 56 | + mpool_free((char*)e, m); | |
| 57 | +} | |
| 58 | + | |
| 59 | +float tEnvelopeFollower_tick(tEnvelopeFollower* const ef, float x) | |
| 60 | +{ | |
| 61 | + _tEnvelopeFollower* e = *ef; | |
| 62 | + | |
| 63 | + if (x < 0.0f ) x = -x; /* Absolute value. */ | |
| 64 | + | |
| 65 | + if ((x >= e->y) && (x > e->a_thresh)) e->y = x; /* If we hit a peak, ride the peak to the top. */ | |
| 66 | + else e->y = e->y * e->d_coeff; /* Else, exponential decay of output. */ | |
| 67 | + | |
| 68 | + //ef->y = envelope_pow[(uint16_t)(ef->y * (float)UINT16_MAX)] * ef->d_coeff; //not quite the right behavior - too much loss of precision? | |
| 69 | + //ef->y = powf(ef->y, 1.000009f) * ef->d_coeff; // too expensive | |
| 70 | + | |
| 71 | + if( e->y < VSF) e->y = 0.0f; | |
| 72 | + | |
| 73 | + return e->y; | |
| 74 | +} | |
| 75 | + | |
| 76 | +int tEnvelopeFollower_decayCoeff(tEnvelopeFollower* const ef, float decayCoeff) | |
| 77 | +{ | |
| 78 | + _tEnvelopeFollower* e = *ef; | |
| 79 | + return e->d_coeff = decayCoeff; | |
| 80 | +} | |
| 81 | + | |
| 82 | +int tEnvelopeFollower_attackThresh(tEnvelopeFollower* const ef, float attackThresh) | |
| 83 | +{ | |
| 84 | + _tEnvelopeFollower* e = *ef; | |
| 85 | + return e->a_thresh = attackThresh; | |
| 86 | +} | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | +// zero crossing detector | |
| 93 | + | |
| 94 | +void tZeroCrossing_init (tZeroCrossing* const zc, int maxWindowSize) | |
| 95 | +{ | |
| 96 | + tZeroCrossing_initToPool (zc, maxWindowSize, &leaf.mempool); | |
| 97 | +} | |
| 98 | +void tZeroCrossing_free (tZeroCrossing* const zc) | |
| 99 | +{ | |
| 100 | + tZeroCrossing_freeFromPool (zc, &leaf.mempool); | |
| 101 | +} | |
| 102 | +void tZeroCrossing_initToPool (tZeroCrossing* const zc, int maxWindowSize, tMempool* const mp) | |
| 103 | +{ | |
| 104 | + _tMempool* m = *mp; | |
| 105 | + _tZeroCrossing* z = *zc = (_tZeroCrossing*) mpool_alloc(sizeof(_tZeroCrossing), m); | |
| 106 | + | |
| 107 | + z->count = 0; | |
| 108 | + z->maxWindowSize = maxWindowSize; | |
| 109 | + z->currentWindowSize = maxWindowSize; | |
| 110 | + z->invCurrentWindowSize = 1.0f / maxWindowSize; | |
| 111 | + z->position = 0; | |
| 112 | + z->prevPosition = maxWindowSize; | |
| 113 | + z->inBuffer = (float*) mpool_calloc(sizeof(float) * maxWindowSize, m); | |
| 114 | + z->countBuffer = (uint16_t*) mpool_calloc(sizeof(uint16_t) * maxWindowSize, m); | |
| 115 | +} | |
| 116 | + | |
| 117 | +void tZeroCrossing_freeFromPool (tZeroCrossing* const zc, tMempool* const mp) | |
| 118 | +{ | |
| 119 | + _tMempool* m = *mp; | |
| 120 | + _tZeroCrossing* z = *zc; | |
| 121 | + mpool_free((char*)z->inBuffer, m); | |
| 122 | + mpool_free((char*)z->countBuffer, m); | |
| 123 | + mpool_free((char*)z, m); | |
| 124 | +} | |
| 125 | + | |
| 126 | +//returns proportion of zero crossings within window size (0.0 would be none in window, 1.0 would be all zero crossings) | |
| 127 | +float tZeroCrossing_tick (tZeroCrossing* const zc, float input) | |
| 128 | +{ | |
| 129 | + _tZeroCrossing* z = *zc; | |
| 130 | + | |
| 131 | + z->inBuffer[z->position] = input; | |
| 132 | + int futurePosition = ((z->position + 1) % z->currentWindowSize); | |
| 133 | + float output = 0.0f; | |
| 134 | + | |
| 135 | + //add new value to count | |
| 136 | + if ((z->inBuffer[z->position] * z->inBuffer[z->prevPosition]) < 0.0f) | |
| 137 | + { | |
| 138 | + //zero crossing happened, add it to the count array | |
| 139 | + z->countBuffer[z->position] = 1; | |
| 140 | + z->count++; | |
| 141 | + } | |
| 142 | + else | |
| 143 | + { | |
| 144 | + z->countBuffer[z->position] = 0; | |
| 145 | + } | |
| 146 | + | |
| 147 | + //remove oldest value from count | |
| 148 | + if (z->countBuffer[futurePosition] > 0) | |
| 149 | + { | |
| 150 | + z->count--; | |
| 151 | + if (z->count < 0) | |
| 152 | + { | |
| 153 | + z->count = 0; | |
| 154 | + } | |
| 155 | + } | |
| 156 | + | |
| 157 | + z->prevPosition = z->position; | |
| 158 | + z->position = futurePosition; | |
| 159 | + | |
| 160 | + output = z->count * z->invCurrentWindowSize; | |
| 161 | + | |
| 162 | + return output; | |
| 163 | +} | |
| 164 | + | |
| 165 | + | |
| 166 | +void tZeroCrossing_setWindow (tZeroCrossing* const zc, float windowSize) | |
| 167 | +{ | |
| 168 | + _tZeroCrossing* z = *zc; | |
| 169 | + if (windowSize <= z->maxWindowSize) | |
| 170 | + { | |
| 171 | + z->currentWindowSize = windowSize; | |
| 172 | + } | |
| 173 | + else | |
| 174 | + { | |
| 175 | + z->currentWindowSize = z->maxWindowSize; | |
| 176 | + } | |
| 177 | + z->invCurrentWindowSize = 1.0f / z->currentWindowSize; | |
| 178 | +} | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | +//=========================================================================== | |
| 185 | +/* Power Follower */ | |
| 186 | +//=========================================================================== | |
| 187 | +void tPowerFollower_init(tPowerFollower* const pf, float factor) | |
| 188 | +{ | |
| 189 | + _tPowerFollower* p = *pf = (_tPowerFollower*) leaf_alloc(sizeof(_tPowerFollower)); | |
| 190 | + | |
| 191 | + p->curr=0.0f; | |
| 192 | + p->factor=factor; | |
| 193 | + p->oneminusfactor=1.0f-factor; | |
| 194 | +} | |
| 195 | + | |
| 196 | +void tPowerFollower_free(tPowerFollower* const pf) | |
| 197 | +{ | |
| 198 | + _tPowerFollower* p = *pf; | |
| 199 | + | |
| 200 | + leaf_free((char*)p); | |
| 201 | +} | |
| 202 | + | |
| 203 | +void tPowerFollower_initToPool (tPowerFollower* const pf, float factor, tMempool* const mp) | |
| 204 | +{ | |
| 205 | + _tMempool* m = *mp; | |
| 206 | + _tPowerFollower* p = *pf = (_tPowerFollower*) mpool_alloc(sizeof(_tPowerFollower), m); | |
| 207 | + | |
| 208 | + p->curr=0.0f; | |
| 209 | + p->factor=factor; | |
| 210 | + p->oneminusfactor=1.0f-factor; | |
| 211 | +} | |
| 212 | + | |
| 213 | +void tPowerFollower_freeFromPool (tPowerFollower* const pf, tMempool* const mp) | |
| 214 | +{ | |
| 215 | + _tMempool* m = *mp; | |
| 216 | + _tPowerFollower* p = *pf; | |
| 217 | + | |
| 218 | + mpool_free((char*)p, m); | |
| 219 | +} | |
| 220 | + | |
| 221 | +int tPowerFollower_setFactor(tPowerFollower* const pf, float factor) | |
| 222 | +{ | |
| 223 | + _tPowerFollower* p = *pf; | |
| 224 | + | |
| 225 | + if (factor<0) factor=0; | |
| 226 | + if (factor>1) factor=1; | |
| 227 | + p->factor=factor; | |
| 228 | + p->oneminusfactor=1.0f-factor; | |
| 229 | + return 0; | |
| 230 | +} | |
| 231 | + | |
| 232 | +float tPowerFollower_tick(tPowerFollower* const pf, float input) | |
| 233 | +{ | |
| 234 | + _tPowerFollower* p = *pf; | |
| 235 | + p->curr = p->factor*input*input+p->oneminusfactor*p->curr; | |
| 236 | + return p->curr; | |
| 237 | +} | |
| 238 | + | |
| 239 | +float tPowerFollower_sample(tPowerFollower* const pf) | |
| 240 | +{ | |
| 241 | + _tPowerFollower* p = *pf; | |
| 242 | + return p->curr; | |
| 243 | +} | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | +//=========================================================================== | |
| 249 | +/* ---------------- env~ - simple envelope follower. ----------------- */ | |
| 250 | +//=========================================================================== | |
| 251 | + | |
| 252 | +void tEnvPD_init(tEnvPD* const xpd, int ws, int hs, int bs) | |
| 253 | +{ | |
| 254 | + _tEnvPD* x = *xpd = (_tEnvPD*) leaf_calloc(sizeof(_tEnvPD)); | |
| 255 | + | |
| 256 | + int period = hs, npoints = ws; | |
| 257 | + | |
| 258 | + int i; | |
| 259 | + | |
| 260 | + if (npoints < 1) npoints = 1024; | |
| 261 | + if (period < 1) period = npoints/2; | |
| 262 | + if (period < npoints / MAXOVERLAP + 1) | |
| 263 | + period = npoints / MAXOVERLAP + 1; | |
| 264 | + | |
| 265 | + x->x_npoints = npoints; | |
| 266 | + x->x_phase = 0; | |
| 267 | + x->x_period = period; | |
| 268 | + | |
| 269 | + x->windowSize = npoints; | |
| 270 | + x->hopSize = period; | |
| 271 | + x->blockSize = bs; | |
| 272 | + | |
| 273 | + for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0.0f; | |
| 274 | + for (i = 0; i < npoints; i++) | |
| 275 | + x->buf[i] = (1.0f - cosf((TWO_PI * i) / npoints))/npoints; | |
| 276 | + for (; i < npoints+INITVSTAKEN; i++) x->buf[i] = 0.0f; | |
| 277 | + | |
| 278 | + x->x_f = 0.0f; | |
| 279 | + | |
| 280 | + x->x_allocforvs = INITVSTAKEN; | |
| 281 | + | |
| 282 | + // ~ ~ ~ dsp ~ ~ ~ | |
| 283 | + if (x->x_period % x->blockSize) | |
| 284 | + { | |
| 285 | + x->x_realperiod = x->x_period + x->blockSize - (x->x_period % x->blockSize); | |
| 286 | + } | |
| 287 | + else | |
| 288 | + { | |
| 289 | + x->x_realperiod = x->x_period; | |
| 290 | + } | |
| 291 | + // ~ ~ ~ ~ ~ ~ ~ ~ | |
| 292 | +} | |
| 293 | + | |
| 294 | +void tEnvPD_free (tEnvPD* const xpd) | |
| 295 | +{ | |
| 296 | + _tEnvPD* x = *xpd; | |
| 297 | + | |
| 298 | + leaf_free((char*)x); | |
| 299 | +} | |
| 300 | + | |
| 301 | +void tEnvPD_initToPool (tEnvPD* const xpd, int ws, int hs, int bs, tMempool* const mp) | |
| 302 | +{ | |
| 303 | + _tMempool* m = *mp; | |
| 304 | + _tEnvPD* x = *xpd = (_tEnvPD*) mpool_calloc(sizeof(_tEnvPD), m); | |
| 305 | + | |
| 306 | + int period = hs, npoints = ws; | |
| 307 | + | |
| 308 | + int i; | |
| 309 | + | |
| 310 | + if (npoints < 1) npoints = 1024; | |
| 311 | + if (period < 1) period = npoints/2; | |
| 312 | + if (period < npoints / MAXOVERLAP + 1) | |
| 313 | + period = npoints / MAXOVERLAP + 1; | |
| 314 | + | |
| 315 | + x->x_npoints = npoints; | |
| 316 | + x->x_phase = 0; | |
| 317 | + x->x_period = period; | |
| 318 | + | |
| 319 | + x->windowSize = npoints; | |
| 320 | + x->hopSize = period; | |
| 321 | + x->blockSize = bs; | |
| 322 | + | |
| 323 | + for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0; | |
| 324 | + for (i = 0; i < npoints; i++) | |
| 325 | + x->buf[i] = (1.0f - cosf((2 * PI * i) / npoints))/npoints; | |
| 326 | + for (; i < npoints+INITVSTAKEN; i++) x->buf[i] = 0; | |
| 327 | + | |
| 328 | + x->x_f = 0; | |
| 329 | + | |
| 330 | + x->x_allocforvs = INITVSTAKEN; | |
| 331 | + | |
| 332 | + // ~ ~ ~ dsp ~ ~ ~ | |
| 333 | + if (x->x_period % x->blockSize) | |
| 334 | + { | |
| 335 | + x->x_realperiod = x->x_period + x->blockSize - (x->x_period % x->blockSize); | |
| 336 | + } | |
| 337 | + else | |
| 338 | + { | |
| 339 | + x->x_realperiod = x->x_period; | |
| 340 | + } | |
| 341 | + // ~ ~ ~ ~ ~ ~ ~ ~ | |
| 342 | +} | |
| 343 | + | |
| 344 | +void tEnvPD_freeFromPool (tEnvPD* const xpd, tMempool* const mp) | |
| 345 | +{ | |
| 346 | + _tMempool* m = *mp; | |
| 347 | + _tEnvPD* x = *xpd; | |
| 348 | + | |
| 349 | + mpool_free((char*)x, m); | |
| 350 | +} | |
| 351 | + | |
| 352 | +float tEnvPD_tick (tEnvPD* const xpd) | |
| 353 | +{ | |
| 354 | + _tEnvPD* x = *xpd; | |
| 355 | + return powtodb(x->x_result); | |
| 356 | +} | |
| 357 | + | |
| 358 | +void tEnvPD_processBlock(tEnvPD* const xpd, float* in) | |
| 359 | +{ | |
| 360 | + _tEnvPD* x = *xpd; | |
| 361 | + | |
| 362 | + int n = x->blockSize; | |
| 363 | + | |
| 364 | + int count; | |
| 365 | + t_sample *sump; | |
| 366 | + in += n; | |
| 367 | + for (count = x->x_phase, sump = x->x_sumbuf; | |
| 368 | + count < x->x_npoints; count += x->x_realperiod, sump++) | |
| 369 | + { | |
| 370 | + t_sample *hp = x->buf + count; | |
| 371 | + t_sample *fp = in; | |
| 372 | + t_sample sum = *sump; | |
| 373 | + int i; | |
| 374 | + | |
| 375 | + for (i = 0; i < n; i++) | |
| 376 | + { | |
| 377 | + fp--; | |
| 378 | + sum += *hp++ * (*fp * *fp); | |
| 379 | + } | |
| 380 | + *sump = sum; | |
| 381 | + } | |
| 382 | + sump[0] = 0; | |
| 383 | + x->x_phase -= n; | |
| 384 | + if (x->x_phase < 0) | |
| 385 | + { | |
| 386 | + x->x_result = x->x_sumbuf[0]; | |
| 387 | + for (count = x->x_realperiod, sump = x->x_sumbuf; | |
| 388 | + count < x->x_npoints; count += x->x_realperiod, sump++) | |
| 389 | + sump[0] = sump[1]; | |
| 390 | + sump[0] = 0; | |
| 391 | + x->x_phase = x->x_realperiod - n; | |
| 392 | + } | |
| 393 | +} | |
| 394 | + | |
| 395 | +//=========================================================================== | |
| 396 | +// ATTACKDETECTION | |
| 397 | +//=========================================================================== | |
| 398 | +/********Private function prototypes**********/ | |
| 399 | +static void atkdtk_init(tAttackDetection* const a, int blocksize, int atk, int rel); | |
| 400 | +static void atkdtk_envelope(tAttackDetection* const a, float *in); | |
| 401 | + | |
| 402 | +/********Constructor/Destructor***************/ | |
| 403 | + | |
| 404 | +void tAttackDetection_init(tAttackDetection* const ad, int blocksize, int atk, int rel) | |
| 405 | +{ | |
| 406 | + *ad = (_tAttackDetection*) leaf_alloc(sizeof(_tAttackDetection)); | |
| 407 | + | |
| 408 | + atkdtk_init(ad, blocksize, atk, rel); | |
| 409 | +} | |
| 410 | + | |
| 411 | +void tAttackDetection_free(tAttackDetection* const ad) | |
| 412 | +{ | |
| 413 | + _tAttackDetection* a = *ad; | |
| 414 | + | |
| 415 | + leaf_free((char*)a); | |
| 416 | +} | |
| 417 | + | |
| 418 | +void tAttackDetection_initToPool (tAttackDetection* const ad, int blocksize, int atk, int rel, tMempool* const mp) | |
| 419 | +{ | |
| 420 | + _tMempool* m = *mp; | |
| 421 | + *ad = (_tAttackDetection*) mpool_alloc(sizeof(_tAttackDetection), m); | |
| 422 | + | |
| 423 | + atkdtk_init(ad, blocksize, atk, rel); | |
| 424 | +} | |
| 425 | + | |
| 426 | +void tAttackDetection_freeFromPool (tAttackDetection* const ad, tMempool* const mp) | |
| 427 | +{ | |
| 428 | + _tMempool* m = *mp; | |
| 429 | + _tAttackDetection* a = *ad; | |
| 430 | + | |
| 431 | + mpool_free((char*)a, m); | |
| 432 | +} | |
| 433 | + | |
| 434 | +/*******Public Functions***********/ | |
| 435 | + | |
| 436 | + | |
| 437 | +void tAttackDetection_setBlocksize(tAttackDetection* const ad, int size) | |
| 438 | +{ | |
| 439 | + _tAttackDetection* a = *ad; | |
| 440 | + | |
| 441 | + if(!((size==64)|(size==128)|(size==256)|(size==512)|(size==1024)|(size==2048))) | |
| 442 | + size = DEFBLOCKSIZE; | |
| 443 | + a->blocksize = size; | |
| 444 | + | |
| 445 | + return; | |
| 446 | + | |
| 447 | +} | |
| 448 | + | |
| 449 | +void tAttackDetection_setSamplerate(tAttackDetection* const ad, int inRate) | |
| 450 | +{ | |
| 451 | + _tAttackDetection* a = *ad; | |
| 452 | + | |
| 453 | + a->samplerate = inRate; | |
| 454 | + | |
| 455 | + //Reset atk and rel to recalculate coeff | |
| 456 | + tAttackDetection_setAttack(ad, a->atk); | |
| 457 | + tAttackDetection_setRelease(ad, a->rel); | |
| 458 | +} | |
| 459 | + | |
| 460 | +void tAttackDetection_setThreshold(tAttackDetection* const ad, float thres) | |
| 461 | +{ | |
| 462 | + _tAttackDetection* a = *ad; | |
| 463 | + a->threshold = thres; | |
| 464 | +} | |
| 465 | + | |
| 466 | +void tAttackDetection_setAttack(tAttackDetection* const ad, int inAtk) | |
| 467 | +{ | |
| 468 | + _tAttackDetection* a = *ad; | |
| 469 | + a->atk = inAtk; | |
| 470 | + a->atk_coeff = pow(0.01, 1.0/(a->atk * a->samplerate * 0.001)); | |
| 471 | +} | |
| 472 | + | |
| 473 | +void tAttackDetection_setRelease(tAttackDetection* const ad, int inRel) | |
| 474 | +{ | |
| 475 | + _tAttackDetection* a = *ad; | |
| 476 | + a->rel = inRel; | |
| 477 | + a->rel_coeff = pow(0.01, 1.0/(a->rel * a->samplerate * 0.001)); | |
| 478 | +} | |
| 479 | + | |
| 480 | + | |
| 481 | +int tAttackDetection_detect(tAttackDetection* const ad, float *in) | |
| 482 | +{ | |
| 483 | + _tAttackDetection* a = *ad; | |
| 484 | + | |
| 485 | + int result; | |
| 486 | + | |
| 487 | + atkdtk_envelope(ad, in); | |
| 488 | + | |
| 489 | + if(a->env >= a->prevAmp*2) //2 times greater = 6dB increase | |
| 490 | + result = 1; | |
| 491 | + else | |
| 492 | + result = 0; | |
| 493 | + | |
| 494 | + a->prevAmp = a->env; | |
| 495 | + | |
| 496 | + return result; | |
| 497 | +} | |
| 498 | + | |
| 499 | +/*******Private Functions**********/ | |
| 500 | + | |
| 501 | +static void atkdtk_init(tAttackDetection* const ad, int blocksize, int atk, int rel) | |
| 502 | +{ | |
| 503 | + _tAttackDetection* a = *ad; | |
| 504 | + | |
| 505 | + a->env = 0; | |
| 506 | + a->blocksize = blocksize; | |
| 507 | + a->threshold = DEFTHRESHOLD; | |
| 508 | + a->samplerate = leaf.sampleRate; | |
| 509 | + a->prevAmp = 0; | |
| 510 | + | |
| 511 | + a->env = 0; | |
| 512 | + | |
| 513 | + tAttackDetection_setAttack(ad, atk); | |
| 514 | + tAttackDetection_setRelease(ad, rel); | |
| 515 | +} | |
| 516 | + | |
| 517 | +static void atkdtk_envelope(tAttackDetection* const ad, float *in) | |
| 518 | +{ | |
| 519 | + _tAttackDetection* a = *ad; | |
| 520 | + | |
| 521 | + int i = 0; | |
| 522 | + float tmp; | |
| 523 | + for(i = 0; i < a->blocksize; ++i){ | |
| 524 | + tmp = fastabsf(in[i]); | |
| 525 | + | |
| 526 | + if(tmp > a->env) | |
| 527 | + a->env = a->atk_coeff * (a->env - tmp) + tmp; | |
| 528 | + else | |
| 529 | + a->env = a->rel_coeff * (a->env - tmp) + tmp; | |
| 530 | + } | |
| 531 | + | |
| 532 | +} | |
| 533 | + | |
| 534 | +//=========================================================================== | |
| 535 | +// SNAC | |
| 536 | +//=========================================================================== | |
| 537 | +/******************************************************************************/ | |
| 538 | +/***************************** private procedures *****************************/ | |
| 539 | +/******************************************************************************/ | |
| 540 | + | |
| 541 | +#define REALFFT mayer_realfft | |
| 542 | +#define REALIFFT mayer_realifft | |
| 543 | + | |
| 544 | +static void snac_analyzeframe(tSNAC* const s); | |
| 545 | +static void snac_autocorrelation(tSNAC* const s); | |
| 546 | +static void snac_normalize(tSNAC* const s); | |
| 547 | +static void snac_pickpeak(tSNAC* const s); | |
| 548 | +static void snac_periodandfidelity(tSNAC* const s); | |
| 549 | +static void snac_biasbuf(tSNAC* const s); | |
| 550 | +static float snac_spectralpeak(tSNAC* const s, float periodlength); | |
| 551 | + | |
| 552 | + | |
| 553 | +/******************************************************************************/ | |
| 554 | +/******************************** constructor, destructor *********************/ | |
| 555 | +/******************************************************************************/ | |
| 556 | + | |
| 557 | + | |
| 558 | +void tSNAC_init(tSNAC* const snac, int overlaparg) | |
| 559 | +{ | |
| 560 | + _tSNAC* s = *snac = (_tSNAC*) leaf_calloc(sizeof(_tSNAC)); | |
| 561 | + | |
| 562 | + s->biasfactor = DEFBIAS; | |
| 563 | + s->timeindex = 0; | |
| 564 | + s->periodindex = 0; | |
| 565 | + s->periodlength = 0.; | |
| 566 | + s->fidelity = 0.; | |
| 567 | + s->minrms = DEFMINRMS; | |
| 568 | + s->framesize = SNAC_FRAME_SIZE; | |
| 569 | + | |
| 570 | + s->inputbuf = (float*) leaf_calloc(sizeof(float) * SNAC_FRAME_SIZE); | |
| 571 | + s->processbuf = (float*) leaf_calloc(sizeof(float) * (SNAC_FRAME_SIZE * 2)); | |
| 572 | + s->spectrumbuf = (float*) leaf_calloc(sizeof(float) * (SNAC_FRAME_SIZE / 2)); | |
| 573 | + s->biasbuf = (float*) leaf_calloc(sizeof(float) * SNAC_FRAME_SIZE); | |
| 574 | + | |
| 575 | + snac_biasbuf(snac); | |
| 576 | + tSNAC_setOverlap(snac, overlaparg); | |
| 577 | +} | |
| 578 | + | |
| 579 | +void tSNAC_free(tSNAC* const snac) | |
| 580 | +{ | |
| 581 | + _tSNAC* s = *snac; | |
| 582 | + | |
| 583 | + leaf_free((char*)s->inputbuf); | |
| 584 | + leaf_free((char*)s->processbuf); | |
| 585 | + leaf_free((char*)s->spectrumbuf); | |
| 586 | + leaf_free((char*)s->biasbuf); | |
| 587 | + leaf_free((char*)s); | |
| 588 | +} | |
| 589 | + | |
| 590 | +void tSNAC_initToPool (tSNAC* const snac, int overlaparg, tMempool* const mp) | |
| 591 | +{ | |
| 592 | + _tMempool* m = *mp; | |
| 593 | + _tSNAC* s = *snac = (_tSNAC*) mpool_alloc(sizeof(_tSNAC), m); | |
| 594 | + | |
| 595 | + s->biasfactor = DEFBIAS; | |
| 596 | + s->timeindex = 0; | |
| 597 | + s->periodindex = 0; | |
| 598 | + s->periodlength = 0.; | |
| 599 | + s->fidelity = 0.; | |
| 600 | + s->minrms = DEFMINRMS; | |
| 601 | + s->framesize = SNAC_FRAME_SIZE; | |
| 602 | + | |
| 603 | + s->inputbuf = (float*) mpool_calloc(sizeof(float) * SNAC_FRAME_SIZE, m); | |
| 604 | + s->processbuf = (float*) mpool_calloc(sizeof(float) * (SNAC_FRAME_SIZE * 2), m); | |
| 605 | + s->spectrumbuf = (float*) mpool_calloc(sizeof(float) * (SNAC_FRAME_SIZE / 2), m); | |
| 606 | + s->biasbuf = (float*) mpool_calloc(sizeof(float) * SNAC_FRAME_SIZE, m); | |
| 607 | + | |
| 608 | + snac_biasbuf(snac); | |
| 609 | + tSNAC_setOverlap(snac, overlaparg); | |
| 610 | +} | |
| 611 | + | |
| 612 | +void tSNAC_freeFromPool (tSNAC* const snac, tMempool* const mp) | |
| 613 | +{ | |
| 614 | + _tMempool* m = *mp; | |
| 615 | + _tSNAC* s = *snac; | |
| 616 | + | |
| 617 | + mpool_free((char*)s->inputbuf, m); | |
| 618 | + mpool_free((char*)s->processbuf, m); | |
| 619 | + mpool_free((char*)s->spectrumbuf, m); | |
| 620 | + mpool_free((char*)s->biasbuf, m); | |
| 621 | + mpool_free((char*)s, m); | |
| 622 | +} | |
| 623 | + | |
| 624 | +/******************************************************************************/ | |
| 625 | +/************************** public access functions****************************/ | |
| 626 | +/******************************************************************************/ | |
| 627 | + | |
| 628 | + | |
| 629 | +void tSNAC_ioSamples(tSNAC* const snac, float *in, float *out, int size) | |
| 630 | +{ | |
| 631 | + _tSNAC* s = *snac; | |
| 632 | + | |
| 633 | + int timeindex = s->timeindex; | |
| 634 | + int mask = s->framesize - 1; | |
| 635 | + int outindex = 0; | |
| 636 | + float *inputbuf = s->inputbuf; | |
| 637 | + float *processbuf = s->processbuf; | |
| 638 | + | |
| 639 | + // call analysis function when it is time | |
| 640 | + if(!(timeindex & (s->framesize / s->overlap - 1))) snac_analyzeframe(snac); | |
| 641 | + | |
| 642 | + while(size--) | |
| 643 | + { | |
| 644 | + inputbuf[timeindex] = *in++; | |
| 645 | + out[outindex++] = processbuf[timeindex++]; | |
| 646 | + timeindex &= mask; | |
| 647 | + } | |
| 648 | + s->timeindex = timeindex; | |
| 649 | +} | |
| 650 | + | |
| 651 | +void tSNAC_setOverlap(tSNAC* const snac, int lap) | |
| 652 | +{ | |
| 653 | + _tSNAC* s = *snac; | |
| 654 | + if(!((lap==1)|(lap==2)|(lap==4)|(lap==8))) lap = DEFOVERLAP; | |
| 655 | + s->overlap = lap; | |
| 656 | +} | |
| 657 | + | |
| 658 | + | |
| 659 | +void tSNAC_setBias(tSNAC* const snac, float bias) | |
| 660 | +{ | |
| 661 | + _tSNAC* s = *snac; | |
| 662 | + if(bias > 1.) bias = 1.; | |
| 663 | + if(bias < 0.) bias = 0.; | |
| 664 | + s->biasfactor = bias; | |
| 665 | + snac_biasbuf(snac); | |
| 666 | + return; | |
| 667 | +} | |
| 668 | + | |
| 669 | + | |
| 670 | +void tSNAC_setMinRMS(tSNAC* const snac, float rms) | |
| 671 | +{ | |
| 672 | + _tSNAC* s = *snac; | |
| 673 | + if(rms > 1.) rms = 1.; | |
| 674 | + if(rms < 0.) rms = 0.; | |
| 675 | + s->minrms = rms; | |
| 676 | + return; | |
| 677 | +} | |
| 678 | + | |
| 679 | + | |
| 680 | +float tSNAC_getPeriod(tSNAC* const snac) | |
| 681 | +{ | |
| 682 | + _tSNAC* s = *snac; | |
| 683 | + return(s->periodlength); | |
| 684 | +} | |
| 685 | + | |
| 686 | + | |
| 687 | +float tSNAC_getFidelity(tSNAC* const snac) | |
| 688 | +{ | |
| 689 | + _tSNAC* s = *snac; | |
| 690 | + return(s->fidelity); | |
| 691 | +} | |
| 692 | + | |
| 693 | + | |
| 694 | +/******************************************************************************/ | |
| 695 | +/***************************** private procedures *****************************/ | |
| 696 | +/******************************************************************************/ | |
| 697 | + | |
| 698 | + | |
| 699 | +// main analysis function | |
| 700 | +static void snac_analyzeframe(tSNAC* const snac) | |
| 701 | +{ | |
| 702 | + _tSNAC* s = *snac; | |
| 703 | + | |
| 704 | + int n, tindex = s->timeindex; | |
| 705 | + int framesize = s->framesize; | |
| 706 | + int mask = framesize - 1; | |
| 707 | + float norm = 1. / sqrt((float)(framesize * 2)); | |
| 708 | + | |
| 709 | + float *inputbuf = s->inputbuf; | |
| 710 | + float *processbuf = s->processbuf; | |
| 711 | + | |
| 712 | + // copy input to processing buffers | |
| 713 | + for(n=0; n<framesize; n++) | |
| 714 | + { | |
| 715 | + processbuf[n] = inputbuf[tindex] * norm; | |
| 716 | + tindex++; | |
| 717 | + tindex &= mask; | |
| 718 | + } | |
| 719 | + | |
| 720 | + // zeropadding | |
| 721 | + for(n=framesize; n<(framesize<<1); n++) processbuf[n] = 0.; | |
| 722 | + | |
| 723 | + // call analysis procedures | |
| 724 | + snac_autocorrelation(snac); | |
| 725 | + snac_normalize(snac); | |
| 726 | + snac_pickpeak(snac); | |
| 727 | + snac_periodandfidelity(snac); | |
| 728 | +} | |
| 729 | + | |
| 730 | + | |
| 731 | +static void snac_autocorrelation(tSNAC* const snac) | |
| 732 | +{ | |
| 733 | + _tSNAC* s = *snac; | |
| 734 | + | |
| 735 | + int n, m; | |
| 736 | + int framesize = s->framesize; | |
| 737 | + int fftsize = framesize * 2; | |
| 738 | + float *processbuf = s->processbuf; | |
| 739 | + float *spectrumbuf = s->spectrumbuf; | |
| 740 | + | |
| 741 | + REALFFT(fftsize, processbuf); | |
| 742 | + | |
| 743 | + // compute power spectrum | |
| 744 | + processbuf[0] *= processbuf[0]; // DC | |
| 745 | + processbuf[framesize] *= processbuf[framesize]; // Nyquist | |
| 746 | + | |
| 747 | + for(n=1; n<framesize; n++) | |
| 748 | + { | |
| 749 | + processbuf[n] = processbuf[n] * processbuf[n] | |
| 750 | + + processbuf[fftsize-n] * processbuf[fftsize-n]; // imag coefficients appear reversed | |
| 751 | + processbuf[fftsize-n] = 0.; | |
| 752 | + } | |
| 753 | + | |
| 754 | + // store power spectrum up to SR/4 for possible later use | |
| 755 | + for(m=0; m<(framesize>>1); m++) | |
| 756 | + { | |
| 757 | + spectrumbuf[m] = processbuf[m]; | |
| 758 | + } | |
| 759 | + | |
| 760 | + // transform power spectrum to autocorrelation function | |
| 761 | + REALIFFT(fftsize, processbuf); | |
| 762 | + return; | |
| 763 | +} | |
| 764 | + | |
| 765 | + | |
| 766 | +static void snac_normalize(tSNAC* const snac) | |
| 767 | +{ | |
| 768 | + _tSNAC* s = *snac; | |
| 769 | + | |
| 770 | + int framesize = s->framesize; | |
| 771 | + int framesizeplustimeindex = s->framesize + s->timeindex; | |
| 772 | + int timeindexminusone = s->timeindex - 1; | |
| 773 | + int n, m; | |
| 774 | + int mask = framesize - 1; | |
| 775 | + int seek = framesize * SEEK; | |
| 776 | + float *inputbuf = s->inputbuf; | |
| 777 | + float *processbuf= s->processbuf; | |
| 778 | + float signal1, signal2; | |
| 779 | + | |
| 780 | + // minimum RMS implemented as minimum autocorrelation at index 0 | |
| 781 | + // functionally equivalent to white noise floor | |
| 782 | + float rms = s->minrms / sqrt(1.0f / (float)framesize); | |
| 783 | + float minrzero = rms * rms; | |
| 784 | + float rzero = processbuf[0]; | |
| 785 | + if(rzero < minrzero) rzero = minrzero; | |
| 786 | + double normintegral = (double)rzero * 2.; | |
| 787 | + | |
| 788 | + // normalize biased autocorrelation function | |
| 789 | + // inputbuf is circular buffer: timeindex may be non-zero when overlap > 1 | |
| 790 | + processbuf[0] = 1; | |
| 791 | + for(n=1, m=s->timeindex+1; n<seek; n++, m++) | |
| 792 | + { | |
| 793 | + signal1 = inputbuf[(n + timeindexminusone)&mask]; | |
| 794 | + signal2 = inputbuf[(framesizeplustimeindex - n)&mask]; | |
| 795 | + normintegral -= (double)(signal1 * signal1 + signal2 * signal2); | |
| 796 | + processbuf[n] /= (float)normintegral * 0.5f; | |
| 797 | + } | |
| 798 | + | |
| 799 | + // flush instable function tail | |
| 800 | + for(n = seek; n<framesize; n++) processbuf[n] = 0.; | |
| 801 | + return; | |
| 802 | +} | |
| 803 | + | |
| 804 | + | |
| 805 | +static void snac_periodandfidelity(tSNAC* const snac) | |
| 806 | +{ | |
| 807 | + _tSNAC* s = *snac; | |
| 808 | + | |
| 809 | + float periodlength; | |
| 810 | + | |
| 811 | + if(s->periodindex) | |
| 812 | + { | |
| 813 | + periodlength = (float)s->periodindex + | |
| 814 | + interpolate3phase(s->processbuf, s->periodindex); | |
| 815 | + if(periodlength < 8) periodlength = snac_spectralpeak(snac, periodlength); | |
| 816 | + s->periodlength = periodlength; | |
| 817 | + s->fidelity = interpolate3max(s->processbuf, s->periodindex); | |
| 818 | + } | |
| 819 | + return; | |
| 820 | +} | |
| 821 | + | |
| 822 | +// select the peak which most probably represents period length | |
| 823 | +static void snac_pickpeak(tSNAC* const snac) | |
| 824 | +{ | |
| 825 | + _tSNAC* s = *snac; | |
| 826 | + | |
| 827 | + int n, peakindex=0; | |
| 828 | + int seek = s->framesize * SEEK; | |
| 829 | + float *processbuf= s->processbuf; | |
| 830 | + float maxvalue = 0.; | |
| 831 | + float biasedpeak; | |
| 832 | + float *biasbuf = s->biasbuf; | |
| 833 | + | |
| 834 | + // skip main lobe | |
| 835 | + for(n=1; n<seek; n++) | |
| 836 | + { | |
| 837 | + if(processbuf[n] < 0.) break; | |
| 838 | + } | |
| 839 | + | |
| 840 | + // find interpolated / biased maximum in SNAC function | |
| 841 | + // interpolation finds the 'real maximum' | |
| 842 | + // biasing favours the first candidate | |
| 843 | + for(; n<seek-1; n++) | |
| 844 | + { | |
| 845 | + if(processbuf[n] >= processbuf[n-1]) | |
| 846 | + { | |
| 847 | + if(processbuf[n] > processbuf[n+1]) // we have a local peak | |
| 848 | + { | |
| 849 | + biasedpeak = interpolate3max(processbuf, n) * biasbuf[n]; | |
| 850 | + | |
| 851 | + if(biasedpeak > maxvalue) | |
| 852 | + { | |
| 853 | + maxvalue = biasedpeak; | |
| 854 | + peakindex = n; | |
| 855 | + } | |
| 856 | + } | |
| 857 | + } | |
| 858 | + } | |
| 859 | + s->periodindex = peakindex; | |
| 860 | + return; | |
| 861 | +} | |
| 862 | + | |
| 863 | + | |
| 864 | +// verify period length via frequency domain (up till SR/4) | |
| 865 | +// frequency domain is more precise than lag domain for period lengths < 8 | |
| 866 | +// argument 'periodlength' is initial estimation from autocorrelation | |
| 867 | +static float snac_spectralpeak(tSNAC* const snac, float periodlength) | |
| 868 | +{ | |
| 869 | + _tSNAC* s = *snac; | |
| 870 | + | |
| 871 | + if(periodlength < 4.0f) return periodlength; | |
| 872 | + | |
| 873 | + float max = 0.; | |
| 874 | + int n, startbin, stopbin, peakbin = 0; | |
| 875 | + int spectrumsize = s->framesize>>1; | |
| 876 | + float *spectrumbuf = s->spectrumbuf; | |
| 877 | + float peaklocation = (float)(s->framesize * 2.0f) / periodlength; | |
| 878 | + | |
| 879 | + startbin = (int)(peaklocation * 0.8f + 0.5f); | |
| 880 | + if(startbin < 1) startbin = 1; | |
| 881 | + stopbin = (int)(peaklocation * 1.25f + 0.5f); | |
| 882 | + if(stopbin >= spectrumsize - 1) stopbin = spectrumsize - 1; | |
| 883 | + | |
| 884 | + for(n=startbin; n<stopbin; n++) | |
| 885 | + { | |
| 886 | + if(spectrumbuf[n] >= spectrumbuf[n-1]) | |
| 887 | + { | |
| 888 | + if(spectrumbuf[n] > spectrumbuf[n+1]) | |
| 889 | + { | |
| 890 | + if(spectrumbuf[n] > max) | |
| 891 | + { | |
| 892 | + max = spectrumbuf[n]; | |
| 893 | + peakbin = n; | |
| 894 | + } | |
| 895 | + } | |
| 896 | + } | |
| 897 | + } | |
| 898 | + | |
| 899 | + // calculate amplitudes in peak region | |
| 900 | + for(n=(peakbin-1); n<(peakbin+2); n++) | |
| 901 | + { | |
| 902 | + spectrumbuf[n] = sqrtf(spectrumbuf[n]); | |
| 903 | + } | |
| 904 | + | |
| 905 | + peaklocation = (float)peakbin + interpolate3phase(spectrumbuf, peakbin); | |
| 906 | + periodlength = (float)(s->framesize * 2.0f) / peaklocation; | |
| 907 | + | |
| 908 | + return periodlength; | |
| 909 | +} | |
| 910 | + | |
| 911 | + | |
| 912 | +// modified logarithmic bias function | |
| 913 | +static void snac_biasbuf(tSNAC* const snac) | |
| 914 | +{ | |
| 915 | + _tSNAC* s = *snac; | |
| 916 | + | |
| 917 | + int n; | |
| 918 | + int maxperiod = (int)(s->framesize * (float)SEEK); | |
| 919 | + float bias = s->biasfactor / logf((float)(maxperiod - 4)); | |
| 920 | + float *biasbuf = s->biasbuf; | |
| 921 | + | |
| 922 | + for(n=0; n<5; n++) // periods < 5 samples can't be tracked | |
| 923 | + { | |
| 924 | + biasbuf[n] = 0.0f; | |
| 925 | + } | |
| 926 | + | |
| 927 | + for(n=5; n<maxperiod; n++) | |
| 928 | + { | |
| 929 | + biasbuf[n] = 1.0f - (float)logf(n - 4) * bias; | |
| 930 | + } | |
| 931 | +} | |
| 932 | + | |
| 933 | +//=========================================================================== | |
| 934 | +// PERIODDETECTION | |
| 935 | +//=========================================================================== | |
| 936 | +void tPeriodDetection_init (tPeriodDetection* const pd, float* in, float* out, int bufSize, int frameSize) | |
| 937 | +{ | |
| 938 | + tPeriodDetection_initToPool(pd, in, out, bufSize, frameSize, &leaf.mempool); | |
| 939 | +} | |
| 940 | + | |
| 941 | +void tPeriodDetection_free (tPeriodDetection* const pd) | |
| 942 | +{ | |
| 943 | + tPeriodDetection_freeFromPool(pd, &leaf.mempool); | |
| 944 | +} | |
| 945 | + | |
| 946 | +void tPeriodDetection_initToPool (tPeriodDetection* const pd, float* in, float* out, int bufSize, int frameSize, tMempool* const mp) | |
| 947 | +{ | |
| 948 | + _tMempool* m = *mp; | |
| 949 | + _tPeriodDetection* p = *pd = (_tPeriodDetection*) mpool_calloc(sizeof(_tPeriodDetection), m); | |
| 950 | + | |
| 951 | + p->inBuffer = in; | |
| 952 | + p->outBuffer = out; | |
| 953 | + p->bufSize = bufSize; | |
| 954 | + p->frameSize = frameSize; | |
| 955 | + p->framesPerBuffer = p->bufSize / p->frameSize; | |
| 956 | + p->curBlock = 1; | |
| 957 | + p->lastBlock = 0; | |
| 958 | + p->index = 0; | |
| 959 | + | |
| 960 | + p->hopSize = DEFHOPSIZE; | |
| 961 | + p->windowSize = DEFWINDOWSIZE; | |
| 962 | + p->fba = FBA; | |
| 963 | + | |
| 964 | + tEnvPD_initToPool(&p->env, p->windowSize, p->hopSize, p->frameSize, mp); | |
| 965 | + | |
| 966 | + tSNAC_initToPool(&p->snac, DEFOVERLAP, mp); | |
| 967 | + | |
| 968 | + p->timeConstant = DEFTIMECONSTANT; | |
| 969 | + p->radius = expf(-1000.0f * p->hopSize * leaf.invSampleRate / p->timeConstant); | |
| 970 | + p->fidelityThreshold = 0.95; | |
| 971 | +} | |
| 972 | + | |
| 973 | +void tPeriodDetection_freeFromPool (tPeriodDetection* const pd, tMempool* const mp) | |
| 974 | +{ | |
| 975 | + _tMempool* m = *mp; | |
| 976 | + _tPeriodDetection* p = *pd; | |
| 977 | + | |
| 978 | + tEnvPD_freeFromPool(&p->env, mp); | |
| 979 | + tSNAC_freeFromPool(&p->snac, mp); | |
| 980 | + mpool_free((char*)p, m); | |
| 981 | +} | |
| 982 | + | |
| 983 | +float tPeriodDetection_tick (tPeriodDetection* pd, float sample) | |
| 984 | +{ | |
| 985 | + _tPeriodDetection* p = *pd; | |
| 986 | + | |
| 987 | + int i, iLast; | |
| 988 | + | |
| 989 | + i = (p->curBlock*p->frameSize); | |
| 990 | + iLast = (p->lastBlock*p->frameSize)+p->index; | |
| 991 | + | |
| 992 | + p->i = i; | |
| 993 | + p->iLast = iLast; | |
| 994 | + | |
| 995 | + p->inBuffer[i+p->index] = sample; | |
| 996 | + | |
| 997 | + p->index++; | |
| 998 | + p->indexstore = p->index; | |
| 999 | + if (p->index >= p->frameSize) | |
| 1000 | + { | |
| 1001 | + p->index = 0; | |
| 1002 | + | |
| 1003 | + tEnvPD_processBlock(&p->env, &(p->inBuffer[i])); | |
| 1004 | + | |
| 1005 | + tSNAC_ioSamples(&p->snac, &(p->inBuffer[i]), &(p->outBuffer[i]), p->frameSize); | |
| 1006 | + float fidelity = tSNAC_getFidelity(&p->snac); | |
| 1007 | + // Fidelity threshold recommended by Katja Vetters is 0.95 for most instruments/voices http://www.katjaas.nl/helmholtz/helmholtz.html | |
| 1008 | + if (fidelity > p->fidelityThreshold) p->period = tSNAC_getPeriod(&p->snac); | |
| 1009 | + | |
| 1010 | + p->curBlock++; | |
| 1011 | + if (p->curBlock >= p->framesPerBuffer) p->curBlock = 0; | |
| 1012 | + p->lastBlock++; | |
| 1013 | + if (p->lastBlock >= p->framesPerBuffer) p->lastBlock = 0; | |
| 1014 | + } | |
| 1015 | + | |
| 1016 | + return p->period; | |
| 1017 | +} | |
| 1018 | + | |
| 1019 | +float tPeriodDetection_getPeriod(tPeriodDetection* pd) | |
| 1020 | +{ | |
| 1021 | + _tPeriodDetection* p = *pd; | |
| 1022 | + return p->period; | |
| 1023 | +} | |
| 1024 | + | |
| 1025 | +void tPeriodDetection_setHopSize(tPeriodDetection* pd, int hs) | |
| 1026 | +{ | |
| 1027 | + _tPeriodDetection* p = *pd; | |
| 1028 | + p->hopSize = hs; | |
| 1029 | +} | |
| 1030 | + | |
| 1031 | +void tPeriodDetection_setWindowSize(tPeriodDetection* pd, int ws) | |
| 1032 | +{ | |
| 1033 | + _tPeriodDetection* p = *pd; | |
| 1034 | + p->windowSize = ws; | |
| 1035 | +} | |
| 1036 | + | |
| 1037 | +void tPeriodDetection_setFidelityThreshold(tPeriodDetection* pd, float threshold) | |
| 1038 | +{ | |
| 1039 | + _tPeriodDetection* p = *pd; | |
| 1040 | + p->fidelityThreshold = threshold; | |
| 1041 | +} | |
| 1042 | + | |
| 1043 | +void tPeriodDetection_setAlpha (tPeriodDetection* pd, float alpha) | |
| 1044 | +{ | |
| 1045 | + _tPeriodDetection* p = *pd; | |
| 1046 | + p->alpha = LEAF_clip(0.0f, alpha, 1.0f); | |
| 1047 | +} | |
| 1048 | + | |
| 1049 | +void tPeriodDetection_setTolerance (tPeriodDetection* pd, float tolerance) | |
| 1050 | +{ | |
| 1051 | + _tPeriodDetection* p = *pd; | |
| 1052 | + if (tolerance < 0.0f) p->tolerance = 0.0f; | |
| 1053 | + else p->tolerance = tolerance; | |
| 1054 | +} | |
| @@ -0,0 +1,928 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-delay.c | |
| 4 | + Created: 20 Jan 2017 12:01:24pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-delay.h" | |
| 12 | +#include "..\leaf.h" | |
| 13 | + | |
| 14 | +#else | |
| 15 | + | |
| 16 | +#include "../Inc/leaf-delay.h" | |
| 17 | +#include "../leaf.h" | |
| 18 | + | |
| 19 | +#endif | |
| 20 | + | |
| 21 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Delay ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 22 | +void tDelay_init (tDelay* const dl, uint32_t delay, uint32_t maxDelay) | |
| 23 | +{ | |
| 24 | + tDelay_initToPool(dl, delay, maxDelay, &leaf.mempool); | |
| 25 | +} | |
| 26 | + | |
| 27 | +void tDelay_free(tDelay* const dl) | |
| 28 | +{ | |
| 29 | + tDelay_freeFromPool(dl, &leaf.mempool); | |
| 30 | +} | |
| 31 | + | |
| 32 | +void tDelay_initToPool (tDelay* const dl, uint32_t delay, uint32_t maxDelay, tMempool* const mp) | |
| 33 | +{ | |
| 34 | + _tMempool* m = *mp; | |
| 35 | + _tDelay* d = *dl = (_tDelay*) mpool_alloc(sizeof(_tDelay), m); | |
| 36 | + | |
| 37 | + d->maxDelay = maxDelay; | |
| 38 | + | |
| 39 | + d->delay = delay; | |
| 40 | + | |
| 41 | + d->buff = (float*) mpool_alloc(sizeof(float) * maxDelay, m); | |
| 42 | + | |
| 43 | + d->inPoint = 0; | |
| 44 | + d->outPoint = 0; | |
| 45 | + | |
| 46 | + d->lastIn = 0.0f; | |
| 47 | + d->lastOut = 0.0f; | |
| 48 | + | |
| 49 | + d->gain = 1.0f; | |
| 50 | + | |
| 51 | + tDelay_setDelay(dl, d->delay); | |
| 52 | +} | |
| 53 | + | |
| 54 | +void tDelay_freeFromPool (tDelay* const dl, tMempool* const mp) | |
| 55 | +{ | |
| 56 | + _tMempool* m = *mp; | |
| 57 | + _tDelay* d = *dl; | |
| 58 | + | |
| 59 | + mpool_free((char*)d->buff, m); | |
| 60 | + mpool_free((char*)d, m); | |
| 61 | +} | |
| 62 | + | |
| 63 | +void tDelay_clear(tDelay* const dl) | |
| 64 | +{ | |
| 65 | + _tDelay* d = *dl; | |
| 66 | + for (int i = 0; i < d->maxDelay; i++) | |
| 67 | + { | |
| 68 | + d->buff[i] = 0; | |
| 69 | + } | |
| 70 | +} | |
| 71 | + | |
| 72 | +float tDelay_tick (tDelay* const dl, float input) | |
| 73 | +{ | |
| 74 | + _tDelay* d = *dl; | |
| 75 | + | |
| 76 | + // Input | |
| 77 | + d->lastIn = input; | |
| 78 | + d->buff[d->inPoint] = input * d->gain; | |
| 79 | + if (++(d->inPoint) == d->maxDelay) d->inPoint = 0; | |
| 80 | + | |
| 81 | + // Output | |
| 82 | + d->lastOut = d->buff[d->outPoint]; | |
| 83 | + if (++(d->outPoint) == d->maxDelay) d->outPoint = 0; | |
| 84 | + | |
| 85 | + return d->lastOut; | |
| 86 | +} | |
| 87 | + | |
| 88 | +int tDelay_setDelay (tDelay* const dl, uint32_t delay) | |
| 89 | +{ | |
| 90 | + _tDelay* d = *dl; | |
| 91 | + | |
| 92 | + d->delay = LEAF_clip(0.0f, delay, d->maxDelay); | |
| 93 | + | |
| 94 | + // read chases write | |
| 95 | + if ( d->inPoint >= delay ) d->outPoint = d->inPoint - d->delay; | |
| 96 | + else d->outPoint = d->maxDelay + d->inPoint - d->delay; | |
| 97 | + | |
| 98 | + return 0; | |
| 99 | +} | |
| 100 | + | |
| 101 | +float tDelay_tapOut (tDelay* const dl, uint32_t tapDelay) | |
| 102 | +{ | |
| 103 | + _tDelay* d = *dl; | |
| 104 | + | |
| 105 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 106 | + | |
| 107 | + // Check for wraparound. | |
| 108 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 109 | + | |
| 110 | + return d->buff[tap]; | |
| 111 | + | |
| 112 | +} | |
| 113 | + | |
| 114 | +void tDelay_tapIn (tDelay* const dl, float value, uint32_t tapDelay) | |
| 115 | +{ | |
| 116 | + _tDelay* d = *dl; | |
| 117 | + | |
| 118 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 119 | + | |
| 120 | + // Check for wraparound. | |
| 121 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 122 | + | |
| 123 | + d->buff[tap] = value; | |
| 124 | +} | |
| 125 | + | |
| 126 | +float tDelay_addTo (tDelay* const dl, float value, uint32_t tapDelay) | |
| 127 | +{ | |
| 128 | + _tDelay* d = *dl; | |
| 129 | + | |
| 130 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 131 | + | |
| 132 | + // Check for wraparound. | |
| 133 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 134 | + | |
| 135 | + return (d->buff[tap] += value); | |
| 136 | +} | |
| 137 | + | |
| 138 | +uint32_t tDelay_getDelay (tDelay* const dl) | |
| 139 | +{ | |
| 140 | + _tDelay* d = *dl; | |
| 141 | + return d->delay; | |
| 142 | +} | |
| 143 | + | |
| 144 | +float tDelay_getLastOut (tDelay* const dl) | |
| 145 | +{ | |
| 146 | + _tDelay* d = *dl; | |
| 147 | + return d->lastOut; | |
| 148 | +} | |
| 149 | + | |
| 150 | +float tDelay_getLastIn (tDelay* const dl) | |
| 151 | +{ | |
| 152 | + _tDelay* d = *dl; | |
| 153 | + return d->lastIn; | |
| 154 | +} | |
| 155 | + | |
| 156 | +void tDelay_setGain (tDelay* const dl, float gain) | |
| 157 | +{ | |
| 158 | + _tDelay* d = *dl; | |
| 159 | + if (gain < 0.0f) d->gain = 0.0f; | |
| 160 | + else d->gain = gain; | |
| 161 | +} | |
| 162 | + | |
| 163 | +float tDelay_getGain (tDelay* const dl) | |
| 164 | +{ | |
| 165 | + _tDelay* d = *dl; | |
| 166 | + return d->gain; | |
| 167 | +} | |
| 168 | + | |
| 169 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ LinearDelay ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 170 | +void tLinearDelay_init (tLinearDelay* const dl, float delay, uint32_t maxDelay) | |
| 171 | +{ | |
| 172 | + tLinearDelay_initToPool(dl, delay, maxDelay, &leaf.mempool); | |
| 173 | +} | |
| 174 | + | |
| 175 | +void tLinearDelay_free(tLinearDelay* const dl) | |
| 176 | +{ | |
| 177 | + tLinearDelay_freeFromPool(dl, &leaf.mempool); | |
| 178 | +} | |
| 179 | + | |
| 180 | +void tLinearDelay_initToPool (tLinearDelay* const dl, float delay, uint32_t maxDelay, tMempool* const mp) | |
| 181 | +{ | |
| 182 | + _tMempool* m = *mp; | |
| 183 | + _tLinearDelay* d = *dl = (_tLinearDelay*) mpool_alloc(sizeof(_tLinearDelay), m); | |
| 184 | + | |
| 185 | + d->maxDelay = maxDelay; | |
| 186 | + | |
| 187 | + if (delay > maxDelay) d->delay = maxDelay; | |
| 188 | + else if (delay < 0.0f) d->delay = 0.0f; | |
| 189 | + else d->delay = delay; | |
| 190 | + | |
| 191 | + d->buff = (float*) mpool_alloc(sizeof(float) * maxDelay, m); | |
| 192 | + | |
| 193 | + d->gain = 1.0f; | |
| 194 | + | |
| 195 | + d->lastIn = 0.0f; | |
| 196 | + d->lastOut = 0.0f; | |
| 197 | + | |
| 198 | + d->inPoint = 0; | |
| 199 | + d->outPoint = 0; | |
| 200 | + | |
| 201 | + tLinearDelay_setDelay(dl, d->delay); | |
| 202 | +} | |
| 203 | + | |
| 204 | +void tLinearDelay_freeFromPool(tLinearDelay* const dl, tMempool* const mp) | |
| 205 | +{ | |
| 206 | + _tMempool* m = *mp; | |
| 207 | + _tLinearDelay* d = *dl; | |
| 208 | + | |
| 209 | + mpool_free((char*)d->buff, m); | |
| 210 | + mpool_free((char*)d, m); | |
| 211 | +} | |
| 212 | + | |
| 213 | +void tLinearDelay_clear(tLinearDelay* const dl) | |
| 214 | +{ | |
| 215 | + _tLinearDelay* d = *dl; | |
| 216 | + for (int i = 0; i < d->maxDelay; i++) | |
| 217 | + { | |
| 218 | + d->buff[i] = 0; | |
| 219 | + } | |
| 220 | +} | |
| 221 | + | |
| 222 | +float tLinearDelay_tick (tLinearDelay* const dl, float input) | |
| 223 | +{ | |
| 224 | + _tLinearDelay* d = *dl; | |
| 225 | + | |
| 226 | + d->buff[d->inPoint] = input * d->gain; | |
| 227 | + | |
| 228 | + // Increment input pointer modulo length. | |
| 229 | + if (++(d->inPoint) == d->maxDelay ) d->inPoint = 0; | |
| 230 | + | |
| 231 | + uint32_t idx = (uint32_t) d->outPoint; | |
| 232 | + // First 1/2 of interpolation | |
| 233 | + d->lastOut = d->buff[idx] * d->omAlpha; | |
| 234 | + // Second 1/2 of interpolation | |
| 235 | + if ((idx + 1) < d->maxDelay) | |
| 236 | + d->lastOut += d->buff[idx+1] * d->alpha; | |
| 237 | + else | |
| 238 | + d->lastOut += d->buff[0] * d->alpha; | |
| 239 | + | |
| 240 | + // Increment output pointer modulo length | |
| 241 | + if ( (++d->outPoint) >= d->maxDelay ) d->outPoint = 0; | |
| 242 | + | |
| 243 | + return d->lastOut; | |
| 244 | +} | |
| 245 | + | |
| 246 | +void tLinearDelay_tickIn (tLinearDelay* const dl, float input) | |
| 247 | +{ | |
| 248 | + _tLinearDelay* d = *dl; | |
| 249 | + | |
| 250 | + d->buff[d->inPoint] = input * d->gain; | |
| 251 | + | |
| 252 | + // Increment input pointer modulo length. | |
| 253 | + if (++(d->inPoint) == d->maxDelay ) d->inPoint = 0; | |
| 254 | +} | |
| 255 | + | |
| 256 | +float tLinearDelay_tickOut (tLinearDelay* const dl) | |
| 257 | +{ | |
| 258 | + _tLinearDelay* d = *dl; | |
| 259 | + | |
| 260 | + uint32_t idx = (uint32_t) d->outPoint; | |
| 261 | + // First 1/2 of interpolation | |
| 262 | + d->lastOut = d->buff[idx] * d->omAlpha; | |
| 263 | + // Second 1/2 of interpolation | |
| 264 | + if ((idx + 1) < d->maxDelay) | |
| 265 | + d->lastOut += d->buff[idx+1] * d->alpha; | |
| 266 | + else | |
| 267 | + d->lastOut += d->buff[0] * d->alpha; | |
| 268 | + | |
| 269 | + // Increment output pointer modulo length | |
| 270 | + if ( (++d->outPoint) >= d->maxDelay ) d->outPoint = 0; | |
| 271 | + | |
| 272 | + return d->lastOut; | |
| 273 | +} | |
| 274 | + | |
| 275 | +int tLinearDelay_setDelay (tLinearDelay* const dl, float delay) | |
| 276 | +{ | |
| 277 | + _tLinearDelay* d = *dl; | |
| 278 | + | |
| 279 | + d->delay = LEAF_clip(0.0f, delay, d->maxDelay); | |
| 280 | + | |
| 281 | + float outPointer = d->inPoint - d->delay; | |
| 282 | + | |
| 283 | + while ( outPointer < 0 ) | |
| 284 | + outPointer += d->maxDelay; // modulo maximum length | |
| 285 | + | |
| 286 | + d->outPoint = (uint32_t) outPointer; // integer part | |
| 287 | + | |
| 288 | + d->alpha = outPointer - d->outPoint; // fractional part | |
| 289 | + d->omAlpha = 1.0f - d->alpha; | |
| 290 | + | |
| 291 | + if ( d->outPoint == d->maxDelay ) d->outPoint = 0; | |
| 292 | + | |
| 293 | + return 0; | |
| 294 | +} | |
| 295 | + | |
| 296 | +float tLinearDelay_tapOut (tLinearDelay* const dl, uint32_t tapDelay) | |
| 297 | +{ | |
| 298 | + _tLinearDelay* d = *dl; | |
| 299 | + | |
| 300 | + uint32_t tap = d->inPoint - tapDelay - 1; | |
| 301 | + // Check for wraparound. | |
| 302 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 303 | + | |
| 304 | + return d->buff[tap]; | |
| 305 | +} | |
| 306 | + | |
| 307 | +void tLinearDelay_tapIn (tLinearDelay* const dl, float value, uint32_t tapDelay) | |
| 308 | +{ | |
| 309 | + _tLinearDelay* d = *dl; | |
| 310 | + | |
| 311 | + uint32_t tap = d->inPoint - tapDelay - 1; | |
| 312 | + | |
| 313 | + // Check for wraparound. | |
| 314 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 315 | + | |
| 316 | + d->buff[tap] = value; | |
| 317 | +} | |
| 318 | + | |
| 319 | +float tLinearDelay_addTo (tLinearDelay* const dl, float value, uint32_t tapDelay) | |
| 320 | +{ | |
| 321 | + _tLinearDelay* d = *dl; | |
| 322 | + | |
| 323 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 324 | + | |
| 325 | + // Check for wraparound. | |
| 326 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 327 | + | |
| 328 | + return (d->buff[tap] += value); | |
| 329 | +} | |
| 330 | + | |
| 331 | +float tLinearDelay_getDelay (tLinearDelay* const dl) | |
| 332 | +{ | |
| 333 | + _tLinearDelay* d = *dl; | |
| 334 | + return d->delay; | |
| 335 | +} | |
| 336 | + | |
| 337 | +float tLinearDelay_getLastOut (tLinearDelay* const dl) | |
| 338 | +{ | |
| 339 | + _tLinearDelay* d = *dl; | |
| 340 | + return d->lastOut; | |
| 341 | +} | |
| 342 | + | |
| 343 | +float tLinearDelay_getLastIn (tLinearDelay* const dl) | |
| 344 | +{ | |
| 345 | + _tLinearDelay* d = *dl; | |
| 346 | + return d->lastIn; | |
| 347 | +} | |
| 348 | + | |
| 349 | +void tLinearDelay_setGain (tLinearDelay* const dl, float gain) | |
| 350 | +{ | |
| 351 | + _tLinearDelay* d = *dl; | |
| 352 | + if (gain < 0.0f) d->gain = 0.0f; | |
| 353 | + else d->gain = gain; | |
| 354 | +} | |
| 355 | + | |
| 356 | +float tLinearDelay_getGain (tLinearDelay* const dl) | |
| 357 | +{ | |
| 358 | + _tLinearDelay* d = *dl; | |
| 359 | + return d->gain; | |
| 360 | +} | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | +/// Hermite Interpolated Delay | |
| 367 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ LinearDelay ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 368 | +void tHermiteDelay_init (tHermiteDelay* const dl, float delay, uint32_t maxDelay) | |
| 369 | +{ | |
| 370 | + tHermiteDelay_initToPool(dl, delay, maxDelay, &leaf.mempool); | |
| 371 | +} | |
| 372 | + | |
| 373 | +void tHermiteDelay_free(tHermiteDelay* const dl) | |
| 374 | +{ | |
| 375 | + tHermiteDelay_freeFromPool(dl, &leaf.mempool); | |
| 376 | +} | |
| 377 | + | |
| 378 | +void tHermiteDelay_initToPool (tHermiteDelay* const dl, float delay, uint32_t maxDelay, tMempool* const mp) | |
| 379 | +{ | |
| 380 | + _tMempool* m = *mp; | |
| 381 | + _tHermiteDelay* d = *dl = (_tHermiteDelay*) mpool_alloc(sizeof(_tHermiteDelay), m); | |
| 382 | + | |
| 383 | + d->maxDelay = maxDelay; | |
| 384 | + | |
| 385 | + if (delay > maxDelay) d->delay = maxDelay; | |
| 386 | + else if (delay < 0.0f) d->delay = 0.0f; | |
| 387 | + else d->delay = delay; | |
| 388 | + | |
| 389 | + d->buff = (float*) mpool_alloc(sizeof(float) * maxDelay, m); | |
| 390 | + | |
| 391 | + d->gain = 1.0f; | |
| 392 | + | |
| 393 | + d->lastIn = 0.0f; | |
| 394 | + d->lastOut = 0.0f; | |
| 395 | + | |
| 396 | + d->inPoint = 0; | |
| 397 | + d->outPoint = 0; | |
| 398 | + | |
| 399 | + tHermiteDelay_setDelay(dl, d->delay); | |
| 400 | +} | |
| 401 | + | |
| 402 | +void tHermiteDelay_freeFromPool(tHermiteDelay* const dl, tMempool* const mp) | |
| 403 | +{ | |
| 404 | + _tMempool* m = *mp; | |
| 405 | + _tHermiteDelay* d = *dl; | |
| 406 | + | |
| 407 | + mpool_free((char*)d->buff, m); | |
| 408 | + mpool_free((char*)d, m); | |
| 409 | +} | |
| 410 | + | |
| 411 | + | |
| 412 | +void tHermiteDelay_clear(tHermiteDelay* const dl) | |
| 413 | +{ | |
| 414 | + _tHermiteDelay* d = *dl; | |
| 415 | + for (int i = 0; i < d->maxDelay; i++) | |
| 416 | + { | |
| 417 | + d->buff[i] = 0; | |
| 418 | + } | |
| 419 | +} | |
| 420 | + | |
| 421 | +float tHermiteDelay_tick (tHermiteDelay* const dl, float input) | |
| 422 | +{ | |
| 423 | + _tHermiteDelay* d = *dl; | |
| 424 | + | |
| 425 | + d->buff[d->inPoint] = input * d->gain; | |
| 426 | + | |
| 427 | + // Increment input pointer modulo length. | |
| 428 | + if (++(d->inPoint) == d->maxDelay ) d->inPoint = 0; | |
| 429 | + | |
| 430 | + | |
| 431 | + uint32_t idx = (uint32_t) d->outPoint; | |
| 432 | + d->lastOut = LEAF_interpolate_hermite (d->buff[((idx - 1) + d->maxDelay) % d->maxDelay], | |
| 433 | + d->buff[idx], | |
| 434 | + d->buff[(idx + 1) % d->maxDelay], | |
| 435 | + d->buff[(idx + 2) % d->maxDelay], | |
| 436 | + d->alpha); | |
| 437 | + | |
| 438 | + // Increment output pointer modulo length | |
| 439 | + if ( (++d->outPoint) >= d->maxDelay ) d->outPoint = 0; | |
| 440 | + | |
| 441 | + return d->lastOut; | |
| 442 | +} | |
| 443 | + | |
| 444 | +void tHermiteDelay_tickIn (tHermiteDelay* const dl, float input) | |
| 445 | +{ | |
| 446 | + _tHermiteDelay* d = *dl; | |
| 447 | + | |
| 448 | + d->buff[d->inPoint] = input * d->gain; | |
| 449 | + | |
| 450 | + // Increment input pointer modulo length. | |
| 451 | + if (++(d->inPoint) == d->maxDelay ) d->inPoint = 0; | |
| 452 | +} | |
| 453 | + | |
| 454 | +float tHermiteDelay_tickOut (tHermiteDelay* const dl) | |
| 455 | +{ | |
| 456 | + _tHermiteDelay* d = *dl; | |
| 457 | + | |
| 458 | + uint32_t idx = (uint32_t) d->outPoint; | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + d->lastOut = LEAF_interpolate_hermite (d->buff[((idx - 1) + d->maxDelay) % d->maxDelay], | |
| 463 | + d->buff[idx], | |
| 464 | + d->buff[(idx + 1) % d->maxDelay], | |
| 465 | + d->buff[(idx + 2) % d->maxDelay], | |
| 466 | + d->alpha); | |
| 467 | + | |
| 468 | + // Increment output pointer modulo length | |
| 469 | + if ( (++d->outPoint) >= d->maxDelay ) d->outPoint = 0; | |
| 470 | + | |
| 471 | + return d->lastOut; | |
| 472 | +} | |
| 473 | + | |
| 474 | +int tHermiteDelay_setDelay (tHermiteDelay* const dl, float delay) | |
| 475 | +{ | |
| 476 | + _tHermiteDelay* d = *dl; | |
| 477 | + | |
| 478 | + d->delay = LEAF_clip(0.0f, delay, d->maxDelay); | |
| 479 | + | |
| 480 | + float outPointer = d->inPoint - d->delay; | |
| 481 | + | |
| 482 | + while ( outPointer < 0 ) | |
| 483 | + outPointer += d->maxDelay; // modulo maximum length | |
| 484 | + | |
| 485 | + d->outPoint = (uint32_t) outPointer; // integer part | |
| 486 | + | |
| 487 | + d->alpha = outPointer - d->outPoint; // fractional part | |
| 488 | + d->omAlpha = 1.0f - d->alpha; | |
| 489 | + | |
| 490 | + if ( d->outPoint == d->maxDelay ) d->outPoint = 0; | |
| 491 | + | |
| 492 | + return 0; | |
| 493 | +} | |
| 494 | + | |
| 495 | +float tHermiteDelay_tapOut (tHermiteDelay* const dl, uint32_t tapDelay) | |
| 496 | +{ | |
| 497 | + _tHermiteDelay* d = *dl; | |
| 498 | + | |
| 499 | + uint32_t tap = d->inPoint - tapDelay - 1; | |
| 500 | + | |
| 501 | + // Check for wraparound. | |
| 502 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 503 | + | |
| 504 | + return d->buff[tap]; | |
| 505 | + | |
| 506 | +} | |
| 507 | + | |
| 508 | +void tHermiteDelay_tapIn (tHermiteDelay* const dl, float value, uint32_t tapDelay) | |
| 509 | +{ | |
| 510 | + _tHermiteDelay* d = *dl; | |
| 511 | + | |
| 512 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 513 | + | |
| 514 | + // Check for wraparound. | |
| 515 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 516 | + | |
| 517 | + d->buff[tap] = value; | |
| 518 | +} | |
| 519 | + | |
| 520 | +float tHermiteDelay_addTo (tHermiteDelay* const dl, float value, uint32_t tapDelay) | |
| 521 | +{ | |
| 522 | + _tHermiteDelay* d = *dl; | |
| 523 | + | |
| 524 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 525 | + | |
| 526 | + // Check for wraparound. | |
| 527 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 528 | + | |
| 529 | + return (d->buff[tap] += value); | |
| 530 | +} | |
| 531 | + | |
| 532 | +float tHermiteDelay_getDelay (tHermiteDelay* const dl) | |
| 533 | +{ | |
| 534 | + _tHermiteDelay* d = *dl; | |
| 535 | + return d->delay; | |
| 536 | +} | |
| 537 | + | |
| 538 | +float tHermiteDelay_getLastOut (tHermiteDelay* const dl) | |
| 539 | +{ | |
| 540 | + _tHermiteDelay* d = *dl; | |
| 541 | + return d->lastOut; | |
| 542 | +} | |
| 543 | + | |
| 544 | +float tHermiteDelay_getLastIn (tHermiteDelay* const dl) | |
| 545 | +{ | |
| 546 | + _tHermiteDelay* d = *dl; | |
| 547 | + return d->lastIn; | |
| 548 | +} | |
| 549 | + | |
| 550 | +void tHermiteDelay_setGain (tHermiteDelay* const dl, float gain) | |
| 551 | +{ | |
| 552 | + _tHermiteDelay* d = *dl; | |
| 553 | + if (gain < 0.0f) d->gain = 0.0f; | |
| 554 | + else d->gain = gain; | |
| 555 | +} | |
| 556 | + | |
| 557 | +float tHermiteDelay_getGain (tHermiteDelay* const dl) | |
| 558 | +{ | |
| 559 | + _tHermiteDelay* d = *dl; | |
| 560 | + return d->gain; | |
| 561 | +} | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ AllpassDelay ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 568 | +void tAllpassDelay_init (tAllpassDelay* const dl, float delay, uint32_t maxDelay) | |
| 569 | +{ | |
| 570 | + tAllpassDelay_initToPool(dl, delay, maxDelay, &leaf.mempool); | |
| 571 | +} | |
| 572 | + | |
| 573 | +void tAllpassDelay_free(tAllpassDelay* const dl) | |
| 574 | +{ | |
| 575 | + tAllpassDelay_freeFromPool(dl, &leaf.mempool); | |
| 576 | +} | |
| 577 | + | |
| 578 | +void tAllpassDelay_initToPool (tAllpassDelay* const dl, float delay, uint32_t maxDelay, tMempool* const mp) | |
| 579 | +{ | |
| 580 | + _tMempool* m = *mp; | |
| 581 | + _tAllpassDelay* d = *dl = (_tAllpassDelay*) mpool_alloc(sizeof(_tAllpassDelay), m); | |
| 582 | + | |
| 583 | + d->maxDelay = maxDelay; | |
| 584 | + | |
| 585 | + if (delay > maxDelay) d->delay = maxDelay; | |
| 586 | + else if (delay < 0.0f) d->delay = 0.0f; | |
| 587 | + else d->delay = delay; | |
| 588 | + | |
| 589 | + d->buff = (float*) mpool_alloc(sizeof(float) * maxDelay, m); | |
| 590 | + | |
| 591 | + d->gain = 1.0f; | |
| 592 | + | |
| 593 | + d->lastIn = 0.0f; | |
| 594 | + d->lastOut = 0.0f; | |
| 595 | + | |
| 596 | + d->inPoint = 0; | |
| 597 | + d->outPoint = 0; | |
| 598 | + | |
| 599 | + tAllpassDelay_setDelay(dl, d->delay); | |
| 600 | + | |
| 601 | + d->apInput = 0.0f; | |
| 602 | +} | |
| 603 | + | |
| 604 | +void tAllpassDelay_freeFromPool(tAllpassDelay* const dl, tMempool* const mp) | |
| 605 | +{ | |
| 606 | + _tMempool* m = *mp; | |
| 607 | + _tAllpassDelay* d = *dl; | |
| 608 | + | |
| 609 | + mpool_free((char*)d->buff, m); | |
| 610 | + mpool_free((char*)d, m); | |
| 611 | +} | |
| 612 | + | |
| 613 | +void tAllpassDelay_clear(tAllpassDelay* const dl) | |
| 614 | +{ | |
| 615 | + _tAllpassDelay* d = *dl; | |
| 616 | + for (int i = 0; i < d->maxDelay; i++) | |
| 617 | + { | |
| 618 | + d->buff[i] = 0; | |
| 619 | + } | |
| 620 | +} | |
| 621 | + | |
| 622 | +float tAllpassDelay_tick (tAllpassDelay* const dl, float input) | |
| 623 | +{ | |
| 624 | + _tAllpassDelay* d = *dl; | |
| 625 | + | |
| 626 | + d->buff[d->inPoint] = input * d->gain; | |
| 627 | + | |
| 628 | + // Increment input pointer modulo length. | |
| 629 | + if ( ++(d->inPoint) >= d->maxDelay ) d->inPoint = 0; | |
| 630 | + | |
| 631 | + // Do allpass interpolation delay. | |
| 632 | + float out = d->lastOut * -d->coeff; | |
| 633 | + out += d->apInput + ( d->coeff * d->buff[d->outPoint] ); | |
| 634 | + d->lastOut = out; | |
| 635 | + | |
| 636 | + // Save allpass input | |
| 637 | + d->apInput = d->buff[d->outPoint]; | |
| 638 | + | |
| 639 | + // Increment output pointer modulo length. | |
| 640 | + if (++(d->outPoint) >= d->maxDelay ) d->outPoint = 0; | |
| 641 | + | |
| 642 | + return d->lastOut; | |
| 643 | +} | |
| 644 | + | |
| 645 | +int tAllpassDelay_setDelay (tAllpassDelay* const dl, float delay) | |
| 646 | +{ | |
| 647 | + _tAllpassDelay* d = *dl; | |
| 648 | + | |
| 649 | + d->delay = LEAF_clip(0.5f, delay, d->maxDelay); | |
| 650 | + | |
| 651 | + // outPoint chases inPoint | |
| 652 | + float outPointer = (float)d->inPoint - d->delay + 1.0f; | |
| 653 | + | |
| 654 | + while ( outPointer < 0 ) outPointer += d->maxDelay; // mod max length | |
| 655 | + | |
| 656 | + d->outPoint = (uint32_t) outPointer; // integer part | |
| 657 | + | |
| 658 | + if ( d->outPoint >= d->maxDelay ) d->outPoint = 0; | |
| 659 | + | |
| 660 | + d->alpha = 1.0f + (float)d->outPoint - outPointer; // fractional part | |
| 661 | + | |
| 662 | + if ( d->alpha < 0.5f ) | |
| 663 | + { | |
| 664 | + // The optimal range for alpha is about 0.5 - 1.5 in order to | |
| 665 | + // achieve the flattest phase delay response. | |
| 666 | + | |
| 667 | + d->outPoint += 1; | |
| 668 | + | |
| 669 | + if ( d->outPoint >= d->maxDelay ) d->outPoint -= d->maxDelay; | |
| 670 | + | |
| 671 | + d->alpha += 1.0f; | |
| 672 | + } | |
| 673 | + | |
| 674 | + d->coeff = (1.0f - d->alpha) / (1.0f + d->alpha); // coefficient for allpass | |
| 675 | + | |
| 676 | + return 0; | |
| 677 | +} | |
| 678 | + | |
| 679 | +float tAllpassDelay_tapOut (tAllpassDelay* const dl, uint32_t tapDelay) | |
| 680 | +{ | |
| 681 | + _tAllpassDelay* d = *dl; | |
| 682 | + | |
| 683 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 684 | + | |
| 685 | + // Check for wraparound. | |
| 686 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 687 | + | |
| 688 | + return d->buff[tap]; | |
| 689 | + | |
| 690 | +} | |
| 691 | + | |
| 692 | +void tAllpassDelay_tapIn (tAllpassDelay* const dl, float value, uint32_t tapDelay) | |
| 693 | +{ | |
| 694 | + _tAllpassDelay* d = *dl; | |
| 695 | + | |
| 696 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 697 | + | |
| 698 | + // Check for wraparound. | |
| 699 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 700 | + | |
| 701 | + d->buff[tap] = value; | |
| 702 | +} | |
| 703 | + | |
| 704 | +float tAllpassDelay_addTo (tAllpassDelay* const dl, float value, uint32_t tapDelay) | |
| 705 | +{ | |
| 706 | + _tAllpassDelay* d = *dl; | |
| 707 | + | |
| 708 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 709 | + | |
| 710 | + // Check for wraparound. | |
| 711 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 712 | + | |
| 713 | + return (d->buff[tap] += value); | |
| 714 | +} | |
| 715 | + | |
| 716 | +float tAllpassDelay_getDelay (tAllpassDelay* const dl) | |
| 717 | +{ | |
| 718 | + _tAllpassDelay* d = *dl; | |
| 719 | + return d->delay; | |
| 720 | +} | |
| 721 | + | |
| 722 | +float tAllpassDelay_getLastOut (tAllpassDelay* const dl) | |
| 723 | +{ | |
| 724 | + _tAllpassDelay* d = *dl; | |
| 725 | + return d->lastOut; | |
| 726 | +} | |
| 727 | + | |
| 728 | +float tAllpassDelay_getLastIn (tAllpassDelay* const dl) | |
| 729 | +{ | |
| 730 | + _tAllpassDelay* d = *dl; | |
| 731 | + return d->lastIn; | |
| 732 | +} | |
| 733 | + | |
| 734 | +void tAllpassDelay_setGain (tAllpassDelay* const dl, float gain) | |
| 735 | +{ | |
| 736 | + _tAllpassDelay* d = *dl; | |
| 737 | + if (gain < 0.0f) d->gain = 0.0f; | |
| 738 | + else d->gain = gain; | |
| 739 | +} | |
| 740 | + | |
| 741 | +float tAllpassDelay_getGain (tAllpassDelay* const dl) | |
| 742 | +{ | |
| 743 | + _tAllpassDelay* d = *dl; | |
| 744 | + return d->gain; | |
| 745 | +} | |
| 746 | + | |
| 747 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ TapeDelay ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 748 | +void tTapeDelay_init (tTapeDelay* const dl, float delay, uint32_t maxDelay) | |
| 749 | +{ | |
| 750 | + tTapeDelay_initToPool(dl, delay, maxDelay, &leaf.mempool); | |
| 751 | +} | |
| 752 | + | |
| 753 | +void tTapeDelay_free(tTapeDelay* const dl) | |
| 754 | +{ | |
| 755 | + tTapeDelay_freeFromPool(dl, &leaf.mempool); | |
| 756 | +} | |
| 757 | + | |
| 758 | +void tTapeDelay_initToPool (tTapeDelay* const dl, float delay, uint32_t maxDelay, tMempool* const mp) | |
| 759 | +{ | |
| 760 | + _tMempool* m = *mp; | |
| 761 | + _tTapeDelay* d = *dl = (_tTapeDelay*) mpool_alloc(sizeof(_tTapeDelay), m); | |
| 762 | + | |
| 763 | + d->maxDelay = maxDelay; | |
| 764 | + | |
| 765 | + d->buff = (float*) mpool_alloc(sizeof(float) * maxDelay, m); | |
| 766 | + | |
| 767 | + d->gain = 1.0f; | |
| 768 | + | |
| 769 | + d->lastIn = 0.0f; | |
| 770 | + d->lastOut = 0.0f; | |
| 771 | + | |
| 772 | + d->idx = 0.0f; | |
| 773 | + d->inc = 1.0f; | |
| 774 | + d->inPoint = 0; | |
| 775 | + | |
| 776 | + tTapeDelay_setDelay(dl, delay); | |
| 777 | +} | |
| 778 | + | |
| 779 | +void tTapeDelay_freeFromPool(tTapeDelay* const dl, tMempool* const mp) | |
| 780 | +{ | |
| 781 | + _tMempool* m = *mp; | |
| 782 | + _tTapeDelay* d = *dl; | |
| 783 | + | |
| 784 | + mpool_free((char*)d->buff, m); | |
| 785 | + mpool_free((char*)d, m); | |
| 786 | +} | |
| 787 | + | |
| 788 | +void tTapeDelay_clear(tTapeDelay* const dl) | |
| 789 | +{ | |
| 790 | + _tTapeDelay* d = *dl; | |
| 791 | + for (int i = 0; i < d->maxDelay; i++) | |
| 792 | + { | |
| 793 | + d->buff[i] = 0; | |
| 794 | + } | |
| 795 | +} | |
| 796 | + | |
| 797 | +//#define SMOOTH_FACTOR 10.f | |
| 798 | + | |
| 799 | +float tTapeDelay_tick (tTapeDelay* const dl, float input) | |
| 800 | +{ | |
| 801 | + _tTapeDelay* d = *dl; | |
| 802 | + | |
| 803 | + d->buff[d->inPoint] = input * d->gain; | |
| 804 | + | |
| 805 | + // Increment input pointer modulo length. | |
| 806 | + if (++(d->inPoint) == d->maxDelay ) d->inPoint = 0; | |
| 807 | + | |
| 808 | + int idx = (int) d->idx; | |
| 809 | + float alpha = d->idx - idx; | |
| 810 | + | |
| 811 | + d->lastOut = LEAF_interpolate_hermite_x (d->buff[((idx - 1) + d->maxDelay) % d->maxDelay], | |
| 812 | + d->buff[idx], | |
| 813 | + d->buff[(idx + 1) % d->maxDelay], | |
| 814 | + d->buff[(idx + 2) % d->maxDelay], | |
| 815 | + alpha); | |
| 816 | + | |
| 817 | + float diff = (d->inPoint - d->idx); | |
| 818 | + while (diff < 0.f) diff += d->maxDelay; | |
| 819 | + | |
| 820 | + d->inc = 1.0f + (diff - d->delay) / d->delay; //* SMOOTH_FACTOR; | |
| 821 | + | |
| 822 | + d->idx += d->inc; | |
| 823 | + | |
| 824 | + if (d->idx >= d->maxDelay) d->idx = 0.0f; | |
| 825 | + | |
| 826 | + if (d->lastOut) | |
| 827 | + return d->lastOut; | |
| 828 | + return 0.0f; | |
| 829 | +} | |
| 830 | + | |
| 831 | +void tTapeDelay_incrementInPoint(tTapeDelay* const dl) | |
| 832 | +{ | |
| 833 | + _tTapeDelay* d = *dl; | |
| 834 | + // Increment input pointer modulo length. | |
| 835 | + if (++(d->inPoint) == d->maxDelay ) d->inPoint = 0; | |
| 836 | +} | |
| 837 | + | |
| 838 | + | |
| 839 | +void tTapeDelay_setRate(tTapeDelay* const dl, float rate) | |
| 840 | +{ | |
| 841 | + _tTapeDelay* d = *dl; | |
| 842 | + d->inc = rate; | |
| 843 | +} | |
| 844 | + | |
| 845 | +void tTapeDelay_setDelay (tTapeDelay* const dl, float delay) | |
| 846 | +{ | |
| 847 | + _tTapeDelay* d = *dl; | |
| 848 | + d->delay = LEAF_clip(1.f, delay, d->maxDelay); | |
| 849 | +} | |
| 850 | + | |
| 851 | +float tTapeDelay_tapOut (tTapeDelay* const dl, float tapDelay) | |
| 852 | +{ | |
| 853 | + _tTapeDelay* d = *dl; | |
| 854 | + | |
| 855 | + float tap = (float) d->inPoint - tapDelay - 1.f; | |
| 856 | + | |
| 857 | + // Check for wraparound. | |
| 858 | + while ( tap < 0.f ) tap += (float)d->maxDelay; | |
| 859 | + | |
| 860 | + int idx = (int) tap; | |
| 861 | + | |
| 862 | + float alpha = tap - idx; | |
| 863 | + | |
| 864 | + float samp = LEAF_interpolate_hermite_x (d->buff[((idx - 1) + d->maxDelay) % d->maxDelay], | |
| 865 | + d->buff[idx], | |
| 866 | + d->buff[(idx + 1) % d->maxDelay], | |
| 867 | + d->buff[(idx + 2) % d->maxDelay], | |
| 868 | + alpha); | |
| 869 | + | |
| 870 | + return samp; | |
| 871 | + | |
| 872 | +} | |
| 873 | + | |
| 874 | +void tTapeDelay_tapIn (tTapeDelay* const dl, float value, uint32_t tapDelay) | |
| 875 | +{ | |
| 876 | + _tTapeDelay* d = *dl; | |
| 877 | + | |
| 878 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 879 | + | |
| 880 | + // Check for wraparound. | |
| 881 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 882 | + | |
| 883 | + d->buff[tap] = value; | |
| 884 | +} | |
| 885 | + | |
| 886 | +float tTapeDelay_addTo (tTapeDelay* const dl, float value, uint32_t tapDelay) | |
| 887 | +{ | |
| 888 | + _tTapeDelay* d = *dl; | |
| 889 | + | |
| 890 | + int32_t tap = d->inPoint - tapDelay - 1; | |
| 891 | + | |
| 892 | + // Check for wraparound. | |
| 893 | + while ( tap < 0 ) tap += d->maxDelay; | |
| 894 | + | |
| 895 | + return (d->buff[tap] += value); | |
| 896 | +} | |
| 897 | + | |
| 898 | +float tTapeDelay_getDelay (tTapeDelay *dl) | |
| 899 | +{ | |
| 900 | + _tTapeDelay* d = *dl; | |
| 901 | + return d->delay; | |
| 902 | +} | |
| 903 | + | |
| 904 | +float tTapeDelay_getLastOut (tTapeDelay* const dl) | |
| 905 | +{ | |
| 906 | + _tTapeDelay* d = *dl; | |
| 907 | + return d->lastOut; | |
| 908 | +} | |
| 909 | + | |
| 910 | +float tTapeDelay_getLastIn (tTapeDelay* const dl) | |
| 911 | +{ | |
| 912 | + _tTapeDelay* d = *dl; | |
| 913 | + return d->lastIn; | |
| 914 | +} | |
| 915 | + | |
| 916 | +void tTapeDelay_setGain (tTapeDelay* const dl, float gain) | |
| 917 | +{ | |
| 918 | + _tTapeDelay* d = *dl; | |
| 919 | + if (gain < 0.0f) d->gain = 0.0f; | |
| 920 | + else d->gain = gain; | |
| 921 | +} | |
| 922 | + | |
| 923 | +float tTapeDelay_getGain (tTapeDelay* const dl) | |
| 924 | +{ | |
| 925 | + _tTapeDelay* d = *dl; | |
| 926 | + return d->gain; | |
| 927 | +} | |
| 928 | + | |
| @@ -0,0 +1,695 @@ | ||
| 1 | +// | |
| 2 | +// leaf-distortion.c | |
| 3 | +// LEAF | |
| 4 | +// | |
| 5 | +// Created by Jeff Snyder, Matthew Wang, Michael Mulshine, and Joshua Becker | |
| 6 | +// Copyright © 2019 Princeton University. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-distortion.h" | |
| 12 | +#include "..\Inc\leaf-tables.h" | |
| 13 | +#else | |
| 14 | + | |
| 15 | + | |
| 16 | +#include "../Inc/leaf-distortion.h" | |
| 17 | +#include "../Inc/leaf-tables.h" | |
| 18 | + | |
| 19 | +#endif | |
| 20 | + | |
| 21 | +//============================================================================================================ | |
| 22 | +// Sample-Rate reducer | |
| 23 | +//============================================================================================================ | |
| 24 | + | |
| 25 | + | |
| 26 | +void tSampleReducer_init(tSampleReducer* const sr) | |
| 27 | +{ | |
| 28 | + _tSampleReducer* s = *sr = (_tSampleReducer*) leaf_alloc(sizeof(_tSampleReducer)); | |
| 29 | + | |
| 30 | + s->invRatio = 1.0f; | |
| 31 | + s->hold = 0.0f; | |
| 32 | + s->count = 0; | |
| 33 | +} | |
| 34 | + | |
| 35 | +void tSampleReducer_free (tSampleReducer* const sr) | |
| 36 | +{ | |
| 37 | + _tSampleReducer* s = *sr; | |
| 38 | + | |
| 39 | + leaf_free((char*)s); | |
| 40 | +} | |
| 41 | + | |
| 42 | +void tSampleReducer_initToPool (tSampleReducer* const sr, tMempool* const mp) | |
| 43 | +{ | |
| 44 | + _tMempool* m = *mp; | |
| 45 | + _tSampleReducer* s = *sr = (_tSampleReducer*) mpool_alloc(sizeof(_tSampleReducer), m); | |
| 46 | + | |
| 47 | + s->invRatio = 1.0f; | |
| 48 | + s->hold = 0.0f; | |
| 49 | + s->count = 0; | |
| 50 | +} | |
| 51 | + | |
| 52 | +void tSampleReducer_freeFromPool (tSampleReducer* const sr, tMempool* const mp) | |
| 53 | +{ | |
| 54 | + _tMempool* m = *mp; | |
| 55 | + _tSampleReducer* s = *sr; | |
| 56 | + | |
| 57 | + mpool_free((char*)s, m); | |
| 58 | +} | |
| 59 | + | |
| 60 | +float tSampleReducer_tick(tSampleReducer* const sr, float input) | |
| 61 | +{ | |
| 62 | + _tSampleReducer* s = *sr; | |
| 63 | + if (s->count > s->invRatio) | |
| 64 | + { | |
| 65 | + s->hold = input; | |
| 66 | + s->count = 0; | |
| 67 | + } | |
| 68 | + | |
| 69 | + s->count++; | |
| 70 | + return s->hold; | |
| 71 | +} | |
| 72 | + | |
| 73 | + | |
| 74 | +void tSampleReducer_setRatio(tSampleReducer* const sr, float ratio) | |
| 75 | +{ | |
| 76 | + _tSampleReducer* s = *sr; | |
| 77 | + if ((ratio <= 1.0f) && (ratio >= 0.0f)) | |
| 78 | + s->invRatio = 1.0f / ratio; | |
| 79 | + | |
| 80 | +} | |
| 81 | + | |
| 82 | +//============================================================================================================ | |
| 83 | +// Oversampler | |
| 84 | +//============================================================================================================ | |
| 85 | +// Latency is equal to the phase length (numTaps / ratio) | |
| 86 | +void tOversampler_init(tOversampler* const osr, int ratio, oBool extraQuality) | |
| 87 | +{ | |
| 88 | + _tOversampler* os = *osr = (_tOversampler*) leaf_alloc(sizeof(_tOversampler)); | |
| 89 | + | |
| 90 | + uint8_t offset = 0; | |
| 91 | + if (extraQuality) offset = 6; | |
| 92 | + if (ratio == 2 || ratio == 4 || | |
| 93 | + ratio == 8 || ratio == 16 || | |
| 94 | + ratio == 32 || ratio == 64) { | |
| 95 | + os->ratio = ratio; | |
| 96 | + int idx = (int)(log2f(os->ratio))-1+offset; | |
| 97 | + os->numTaps = __leaf_tablesize_firNumTaps[idx]; | |
| 98 | + os->phaseLength = os->numTaps / os->ratio; | |
| 99 | + os->pCoeffs = (float*) __leaf_tableref_firCoeffs[idx]; | |
| 100 | + os->upState = (float*) leaf_alloc(sizeof(float) * os->numTaps * 2); | |
| 101 | + os->downState = (float*) leaf_alloc(sizeof(float) * os->numTaps * 2); | |
| 102 | + } | |
| 103 | +} | |
| 104 | + | |
| 105 | +void tOversampler_free(tOversampler* const osr) | |
| 106 | +{ | |
| 107 | + _tOversampler* os = *osr; | |
| 108 | + | |
| 109 | + leaf_free((char*)os->upState); | |
| 110 | + leaf_free((char*)os->downState); | |
| 111 | + leaf_free((char*)os); | |
| 112 | +} | |
| 113 | + | |
| 114 | +void tOversampler_initToPool (tOversampler* const osr, int ratio, oBool extraQuality, tMempool* const mp) | |
| 115 | +{ | |
| 116 | + _tMempool* m = *mp; | |
| 117 | + _tOversampler* os = *osr = (_tOversampler*) mpool_alloc(sizeof(_tOversampler), m); | |
| 118 | + | |
| 119 | + uint8_t offset = 0; | |
| 120 | + if (extraQuality) offset = 6; | |
| 121 | + if (ratio == 2 || ratio == 4 || | |
| 122 | + ratio == 8 || ratio == 16 || | |
| 123 | + ratio == 32 || ratio == 64) { | |
| 124 | + os->ratio = ratio; | |
| 125 | + int idx = (int)(log2f(os->ratio))-1+offset; | |
| 126 | + os->numTaps = __leaf_tablesize_firNumTaps[idx]; | |
| 127 | + os->phaseLength = os->numTaps / os->ratio; | |
| 128 | + os->pCoeffs = (float*) __leaf_tableref_firCoeffs[idx]; | |
| 129 | + os->upState = (float*) mpool_alloc(sizeof(float) * os->numTaps * 2, m); | |
| 130 | + os->downState = (float*) mpool_alloc(sizeof(float) * os->numTaps * 2, m); | |
| 131 | + } | |
| 132 | +} | |
| 133 | + | |
| 134 | +void tOversampler_freeFromPool (tOversampler* const osr, tMempool* const mp) | |
| 135 | +{ | |
| 136 | + _tMempool* m = *mp; | |
| 137 | + _tOversampler* os = *osr; | |
| 138 | + | |
| 139 | + mpool_free((char*)os->upState, m); | |
| 140 | + mpool_free((char*)os->downState, m); | |
| 141 | + mpool_free((char*)os, m); | |
| 142 | +} | |
| 143 | + | |
| 144 | +float tOversampler_tick(tOversampler* const osr, float input, float (*effectTick)(float)) | |
| 145 | +{ | |
| 146 | + _tOversampler* os = *osr; | |
| 147 | + | |
| 148 | + float buf[os->ratio]; | |
| 149 | + | |
| 150 | + tOversampler_upsample(osr, input, buf); | |
| 151 | + | |
| 152 | + for (int i = 0; i < os->ratio; ++i) { | |
| 153 | + buf[i] = effectTick(buf[i]); | |
| 154 | + } | |
| 155 | + | |
| 156 | + return tOversampler_downsample(osr, buf); | |
| 157 | +} | |
| 158 | + | |
| 159 | +// From CMSIS DSP Library | |
| 160 | +void tOversampler_upsample(tOversampler* const osr, float input, float* output) | |
| 161 | +{ | |
| 162 | + _tOversampler* os = *osr; | |
| 163 | + | |
| 164 | + float *pState = os->upState; /* State pointer */ | |
| 165 | + float *pCoeffs = os->pCoeffs; /* Coefficient pointer */ | |
| 166 | + float *pStateCur; | |
| 167 | + float *ptr1; /* Temporary pointer for state buffer */ | |
| 168 | + float *ptr2; /* Temporary pointer for coefficient buffer */ | |
| 169 | + float sum0; /* Accumulators */ | |
| 170 | + uint32_t i, tapCnt; /* Loop counters */ | |
| 171 | + uint32_t phaseLen = os->phaseLength; /* Length of each polyphase filter component */ | |
| 172 | + uint32_t j; | |
| 173 | + | |
| 174 | + /* os->pState buffer contains previous frame (phaseLen - 1) samples */ | |
| 175 | + /* pStateCur points to the location where the new input data should be written */ | |
| 176 | + pStateCur = os->upState + (phaseLen - 1U); | |
| 177 | + | |
| 178 | + /* Copy new input sample into the state buffer */ | |
| 179 | + *pStateCur = input; | |
| 180 | + | |
| 181 | + /* Address modifier index of coefficient buffer */ | |
| 182 | + j = 1U; | |
| 183 | + | |
| 184 | + /* Loop over the Interpolation factor. */ | |
| 185 | + i = os->ratio; | |
| 186 | + | |
| 187 | + while (i > 0U) | |
| 188 | + { | |
| 189 | + /* Set accumulator to zero */ | |
| 190 | + sum0 = 0.0f; | |
| 191 | + | |
| 192 | + /* Initialize state pointer */ | |
| 193 | + ptr1 = pState; | |
| 194 | + | |
| 195 | + /* Initialize coefficient pointer */ | |
| 196 | + ptr2 = pCoeffs + (os->ratio - j); | |
| 197 | + | |
| 198 | + /* Loop over the polyPhase length. | |
| 199 | + Repeat until we've computed numTaps-(4*os->L) coefficients. */ | |
| 200 | + | |
| 201 | + /* Initialize tapCnt with number of samples */ | |
| 202 | + tapCnt = phaseLen; | |
| 203 | + | |
| 204 | + while (tapCnt > 0U) | |
| 205 | + { | |
| 206 | + /* Perform the multiply-accumulate */ | |
| 207 | + sum0 += *ptr1++ * *ptr2; | |
| 208 | + | |
| 209 | + /* Upsampling is done by stuffing L-1 zeros between each sample. | |
| 210 | + * So instead of multiplying zeros with coefficients, | |
| 211 | + * Increment the coefficient pointer by interpolation factor times. */ | |
| 212 | + ptr2 += os->ratio; | |
| 213 | + | |
| 214 | + /* Decrement loop counter */ | |
| 215 | + tapCnt--; | |
| 216 | + } | |
| 217 | + | |
| 218 | + /* The result is in the accumulator, store in the destination buffer. */ | |
| 219 | + *output++ = sum0 * os->ratio; | |
| 220 | + | |
| 221 | + /* Increment the address modifier index of coefficient buffer */ | |
| 222 | + j++; | |
| 223 | + | |
| 224 | + /* Decrement the loop counter */ | |
| 225 | + i--; | |
| 226 | + } | |
| 227 | + | |
| 228 | + /* Advance the state pointer by 1 | |
| 229 | + * to process the next group of interpolation factor number samples */ | |
| 230 | + pState = pState + 1; | |
| 231 | + | |
| 232 | + /* Processing is complete. | |
| 233 | + Now copy the last phaseLen - 1 samples to the satrt of the state buffer. | |
| 234 | + This prepares the state buffer for the next function call. */ | |
| 235 | + | |
| 236 | + /* Points to the start of the state buffer */ | |
| 237 | + pStateCur = os->upState; | |
| 238 | + | |
| 239 | + /* Initialize tapCnt with number of samples */ | |
| 240 | + tapCnt = (phaseLen - 1U); | |
| 241 | + | |
| 242 | + /* Copy data */ | |
| 243 | + while (tapCnt > 0U) | |
| 244 | + { | |
| 245 | + *pStateCur++ = *pState++; | |
| 246 | + | |
| 247 | + /* Decrement loop counter */ | |
| 248 | + tapCnt--; | |
| 249 | + } | |
| 250 | +} | |
| 251 | + | |
| 252 | +// From CMSIS DSP Library | |
| 253 | +float tOversampler_downsample(tOversampler *const osr, float* input) | |
| 254 | +{ | |
| 255 | + _tOversampler* os = *osr; | |
| 256 | + | |
| 257 | + float *pState = os->downState; /* State pointer */ | |
| 258 | + float *pCoeffs = os->pCoeffs; /* Coefficient pointer */ | |
| 259 | + float *pStateCur; /* Points to the current sample of the state */ | |
| 260 | + float *px0; /* Temporary pointer for state buffer */ | |
| 261 | + float *pb; /* Temporary pointer for coefficient buffer */ | |
| 262 | + float x0, c0; /* Temporary variables to hold state and coefficient values */ | |
| 263 | + float acc0; /* Accumulator */ | |
| 264 | + uint32_t numTaps = os->numTaps; /* Number of filter coefficients in the filter */ | |
| 265 | + uint32_t i, tapCnt; | |
| 266 | + float output; | |
| 267 | + | |
| 268 | + /* os->pState buffer contains previous frame (numTaps - 1) samples */ | |
| 269 | + /* pStateCur points to the location where the new input data should be written */ | |
| 270 | + pStateCur = os->downState + (numTaps - 1U); | |
| 271 | + | |
| 272 | + /* Copy decimation factor number of new input samples into the state buffer */ | |
| 273 | + i = os->ratio; | |
| 274 | + | |
| 275 | + do | |
| 276 | + { | |
| 277 | + *pStateCur++ = *input++; | |
| 278 | + | |
| 279 | + } while (--i); | |
| 280 | + | |
| 281 | + /* Set accumulator to zero */ | |
| 282 | + acc0 = 0.0f; | |
| 283 | + | |
| 284 | + /* Initialize state pointer */ | |
| 285 | + px0 = pState; | |
| 286 | + | |
| 287 | + /* Initialize coeff pointer */ | |
| 288 | + pb = pCoeffs; | |
| 289 | + | |
| 290 | + /* Initialize tapCnt with number of taps */ | |
| 291 | + tapCnt = numTaps; | |
| 292 | + | |
| 293 | + while (tapCnt > 0U) | |
| 294 | + { | |
| 295 | + /* Read coefficients */ | |
| 296 | + c0 = *pb++; | |
| 297 | + | |
| 298 | + /* Fetch 1 state variable */ | |
| 299 | + x0 = *px0++; | |
| 300 | + | |
| 301 | + /* Perform the multiply-accumulate */ | |
| 302 | + acc0 += x0 * c0; | |
| 303 | + | |
| 304 | + /* Decrement loop counter */ | |
| 305 | + tapCnt--; | |
| 306 | + } | |
| 307 | + | |
| 308 | + /* Advance the state pointer by the decimation factor | |
| 309 | + * to process the next group of decimation factor number samples */ | |
| 310 | + pState = pState + os->ratio; | |
| 311 | + | |
| 312 | + /* The result is in the accumulator, store in the destination buffer. */ | |
| 313 | + output = acc0; | |
| 314 | + | |
| 315 | + /* Processing is complete. | |
| 316 | + Now copy the last numTaps - 1 samples to the start of the state buffer. | |
| 317 | + This prepares the state buffer for the next function call. */ | |
| 318 | + | |
| 319 | + /* Points to the start of the state buffer */ | |
| 320 | + pStateCur = os->downState; | |
| 321 | + | |
| 322 | + /* Initialize tapCnt with number of taps */ | |
| 323 | + tapCnt = (numTaps - 1U); | |
| 324 | + | |
| 325 | + /* Copy data */ | |
| 326 | + while (tapCnt > 0U) | |
| 327 | + { | |
| 328 | + *pStateCur++ = *pState++; | |
| 329 | + | |
| 330 | + /* Decrement loop counter */ | |
| 331 | + tapCnt--; | |
| 332 | + } | |
| 333 | + | |
| 334 | + return output; | |
| 335 | +} | |
| 336 | + | |
| 337 | +int tOversampler_getLatency(tOversampler* const osr) | |
| 338 | +{ | |
| 339 | + _tOversampler* os = *osr; | |
| 340 | + return os->phaseLength; | |
| 341 | +} | |
| 342 | + | |
| 343 | +//============================================================================================================ | |
| 344 | +// WAVEFOLDER | |
| 345 | +//============================================================================================================ | |
| 346 | + | |
| 347 | + | |
| 348 | +//from the paper: Virtual Analog Model of the Lockhart Wavefolder | |
| 349 | +//by Fabián Esqueda, Henri Pöntynen, Julian D. Parker and Stefan Bilbao | |
| 350 | + | |
| 351 | +void tLockhartWavefolder_init(tLockhartWavefolder* const wf) | |
| 352 | +{ | |
| 353 | + tLockhartWavefolder_initToPool (wf, &leaf.mempool); | |
| 354 | +} | |
| 355 | + | |
| 356 | +void tLockhartWavefolder_free(tLockhartWavefolder* const wf) | |
| 357 | +{ | |
| 358 | + _tLockhartWavefolder* w = *wf; | |
| 359 | + | |
| 360 | + leaf_free((char*)w); | |
| 361 | +} | |
| 362 | + | |
| 363 | +void tLockhartWavefolder_initToPool (tLockhartWavefolder* const wf, tMempool* const mp) | |
| 364 | +{ | |
| 365 | + _tMempool* m = *mp; | |
| 366 | + _tLockhartWavefolder* w = *wf = (_tLockhartWavefolder*) mpool_alloc(sizeof(_tLockhartWavefolder), m); | |
| 367 | + | |
| 368 | + w->Ln1 = 0.0; | |
| 369 | + w->Fn1 = 0.0; | |
| 370 | + w->xn1 = 0.0; | |
| 371 | + | |
| 372 | + w->RL = 7.5e3; | |
| 373 | + w->R = 15e3; | |
| 374 | + w->VT = 26e-3; | |
| 375 | + w->Is = 10e-16; | |
| 376 | + | |
| 377 | + w->a = 2.0*w->RL/w->R; | |
| 378 | + w->b = (w->R+2.0*w->RL)/(w->VT*w->R); | |
| 379 | + w->d = (w->RL*w->Is)/w->VT; | |
| 380 | + w->half_a = 0.5 * w->a; | |
| 381 | + w->longthing = (0.5*w->VT/w->b); | |
| 382 | + | |
| 383 | + | |
| 384 | + // Antialiasing error threshold | |
| 385 | + w->AAthresh = 10e-10; //10 | |
| 386 | + | |
| 387 | + w->LambertThresh = 10e-10; //12 //was 8 | |
| 388 | + | |
| 389 | + | |
| 390 | + w->w = 0.0f; | |
| 391 | + w->expw = 0.0f; | |
| 392 | + w->p = 0.0f; | |
| 393 | + w->r = 0.0f; | |
| 394 | + w->s= 0.0f; | |
| 395 | + w->myerr = 0.0f; | |
| 396 | + w->l = 0.0f; | |
| 397 | + w->u = 0.0f; | |
| 398 | + w->Ln = 0.0f; | |
| 399 | + w->Fn = 0.0f; | |
| 400 | + w->tempsDenom = 0.0f; | |
| 401 | + w->tempErrDenom = 0.0f; | |
| 402 | + w->tempOutDenom = 0.0f; | |
| 403 | + | |
| 404 | + | |
| 405 | +} | |
| 406 | + | |
| 407 | +void tLockhartWavefolder_freeFromPool (tLockhartWavefolder* const wf, tMempool* const mp) | |
| 408 | +{ | |
| 409 | + _tMempool* m = *mp; | |
| 410 | + _tLockhartWavefolder* w = *wf; | |
| 411 | + | |
| 412 | + mpool_free((char*)w, m); | |
| 413 | +} | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | +double tLockhartWavefolderLambert(tLockhartWavefolder* const wf, double x, double ln) | |
| 418 | +{ | |
| 419 | + _tLockhartWavefolder* mwf = *wf; | |
| 420 | + | |
| 421 | + | |
| 422 | + // Initial guess (use previous value) | |
| 423 | + mwf->w = ln; | |
| 424 | + | |
| 425 | + // Haley's method (Sec. 4.2 of the paper) | |
| 426 | + for(int i=0; i<3000; i+=1) { //1000 | |
| 427 | + | |
| 428 | + mwf->expw = exp(mwf->w); | |
| 429 | + /* | |
| 430 | + if (isinf(mwf->expw) || isnan(mwf->expw)) | |
| 431 | + { | |
| 432 | + mwf->expw = 10e-5; | |
| 433 | + LEAF_error(); | |
| 434 | + } | |
| 435 | + */ | |
| 436 | + mwf->p = mwf->w*mwf->expw - x; | |
| 437 | + /* | |
| 438 | + if (isinf(mwf->p) || isnan(mwf->p)) | |
| 439 | + { | |
| 440 | + mwf->p = 10e-5; | |
| 441 | + LEAF_error(); | |
| 442 | + } | |
| 443 | + */ | |
| 444 | + mwf->r = (mwf->w+1.0)*mwf->expw; | |
| 445 | + /* | |
| 446 | + if (isinf(mwf->r) || isnan(mwf->r)) | |
| 447 | + { | |
| 448 | + mwf->r = 10e-5; | |
| 449 | + LEAF_error(); | |
| 450 | + } | |
| 451 | + */ | |
| 452 | + mwf->tempsDenom = (2.0*(mwf->w+1.0)); | |
| 453 | + /* | |
| 454 | + if ((mwf->tempsDenom == 0.0) || isinf(mwf->tempsDenom) || isnan(mwf->tempsDenom)) | |
| 455 | + { | |
| 456 | + mwf->tempsDenom = 10e-5; | |
| 457 | + LEAF_error(); | |
| 458 | + } | |
| 459 | + */ | |
| 460 | + mwf->s = (mwf->w+2.0)/mwf->tempsDenom; | |
| 461 | + /* | |
| 462 | + if (isnan(mwf->s) || isinf(mwf->s)) | |
| 463 | + { | |
| 464 | + mwf->s = 10e-5; | |
| 465 | + LEAF_error(); | |
| 466 | + } | |
| 467 | + */ | |
| 468 | + mwf->tempErrDenom = (mwf->r-(mwf->p*mwf->s)); | |
| 469 | + /* | |
| 470 | + if ((mwf->tempErrDenom == 0.0) || isinf(mwf->tempErrDenom) || isnan(mwf->tempErrDenom)) | |
| 471 | + { | |
| 472 | + mwf->tempErrDenom = 10e-5; | |
| 473 | + LEAF_error(); | |
| 474 | + } | |
| 475 | + */ | |
| 476 | + mwf->myerr = (mwf->p/mwf->tempErrDenom); | |
| 477 | + /* | |
| 478 | + if (isnan(mwf->myerr) || isinf(mwf->myerr)) | |
| 479 | + { | |
| 480 | + mwf->myerr = 10e-5; | |
| 481 | + LEAF_error(); | |
| 482 | + } | |
| 483 | + */ | |
| 484 | + | |
| 485 | + if ((fabs(mwf->myerr))<mwf->LambertThresh) { | |
| 486 | + | |
| 487 | + break; | |
| 488 | + } | |
| 489 | + | |
| 490 | + mwf->w = mwf->w - mwf->myerr; | |
| 491 | + | |
| 492 | + /* | |
| 493 | + if (isinf(mwf->w) || isnan(mwf->w)) | |
| 494 | + { | |
| 495 | + mwf->w = 10e-5; | |
| 496 | + LEAF_error(); | |
| 497 | + } | |
| 498 | +*/ | |
| 499 | + | |
| 500 | + } | |
| 501 | + return mwf->w; | |
| 502 | +} | |
| 503 | + | |
| 504 | +float tLockhartWavefolder_tick(tLockhartWavefolder* const wf, float in) | |
| 505 | +{ | |
| 506 | + _tLockhartWavefolder* w = *wf; | |
| 507 | + | |
| 508 | + float out = 0.0f; | |
| 509 | + | |
| 510 | + // Compute Antiderivative | |
| 511 | + w->l = (in > 0.0) - (in < 0.0); | |
| 512 | + w->u = w->d*exp(w->l*w->b*in); | |
| 513 | + /* | |
| 514 | + if ((w->u == 0.0) || isinf(w->u) || isnan(w->u)) | |
| 515 | + { | |
| 516 | + w->u = 10e-5; | |
| 517 | + LEAF_error(); | |
| 518 | + } | |
| 519 | + */ | |
| 520 | + | |
| 521 | + w->Ln = tLockhartWavefolderLambert(wf,w->u,w->Ln1); | |
| 522 | + /* | |
| 523 | + if ((w->Ln == 0.0) || isinf(w->Ln) || isnan(w->Ln)) | |
| 524 | + { | |
| 525 | + w->Ln = 10e-5; | |
| 526 | + LEAF_error(); | |
| 527 | + } | |
| 528 | +*/ | |
| 529 | + w->Fn = (w->longthing*(w->Ln*(w->Ln + 2.0))) - (w->half_a*in*in); | |
| 530 | + /* | |
| 531 | + if ((w->Fn == 0.0) || isinf(w->Fn) || isnan(w->Fn)) | |
| 532 | + { | |
| 533 | + w->Fn = 10e-5; | |
| 534 | + LEAF_error(); | |
| 535 | + } | |
| 536 | + */ | |
| 537 | + // Check for ill-conditioning | |
| 538 | + | |
| 539 | + if (fabs(in-w->xn1)<w->AAthresh) | |
| 540 | + { | |
| 541 | + | |
| 542 | + // Compute Averaged Wavefolder Output | |
| 543 | + double xn = 0.5*(in+w->xn1); | |
| 544 | + w->u = w->d*exp(w->l*w->b*xn); | |
| 545 | + /* | |
| 546 | + if ((w->u == 0.0) || isinf(w->u) || isnan(w->u)) | |
| 547 | + { | |
| 548 | + w->u = 10e-5; | |
| 549 | + LEAF_error(); | |
| 550 | + | |
| 551 | + } | |
| 552 | + */ | |
| 553 | + w->Ln = tLockhartWavefolderLambert(wf,w->u,w->Ln1); | |
| 554 | + /* | |
| 555 | + if ((w->Ln == 0.0) || isinf(w->Ln) || isnan(w->Ln)) | |
| 556 | + { | |
| 557 | + w->Ln = 10e-5; | |
| 558 | + LEAF_error(); | |
| 559 | + } | |
| 560 | + */ | |
| 561 | + out = (float)((w->l*w->VT*w->Ln) - (w->a*xn)); | |
| 562 | + | |
| 563 | + } | |
| 564 | + else | |
| 565 | + { | |
| 566 | + | |
| 567 | + // Apply AA Form | |
| 568 | + w->tempOutDenom = (in-w->xn1); | |
| 569 | + /* | |
| 570 | + if ((w->tempOutDenom == 0.0) || isinf(w->tempOutDenom)) | |
| 571 | + { | |
| 572 | + w->tempOutDenom = 10e-5; | |
| 573 | + LEAF_error(); | |
| 574 | + } | |
| 575 | + */ | |
| 576 | + out = ((w->Fn-w->Fn1)/w->tempOutDenom); | |
| 577 | + /* | |
| 578 | + if (isinf(out) || isnan(out)) | |
| 579 | + { | |
| 580 | + out = 10e-5; | |
| 581 | + LEAF_error(); | |
| 582 | + } | |
| 583 | + */ | |
| 584 | + | |
| 585 | + } | |
| 586 | + | |
| 587 | + // Update States | |
| 588 | + w->Ln1 = w->Ln; | |
| 589 | + w->Fn1 = w->Fn; | |
| 590 | + w->xn1 = (double)in; | |
| 591 | + | |
| 592 | + return out; | |
| 593 | +} | |
| 594 | + | |
| 595 | +//============================================================================================================ | |
| 596 | +// CRUSHER | |
| 597 | +//============================================================================================================ | |
| 598 | +#define SCALAR 5000.f | |
| 599 | + | |
| 600 | +void tCrusher_init (tCrusher* const cr) | |
| 601 | +{ | |
| 602 | + _tCrusher* c = *cr = (_tCrusher*) leaf_alloc(sizeof(_tCrusher)); | |
| 603 | + | |
| 604 | + c->op = 4; | |
| 605 | + c->div = SCALAR; | |
| 606 | + c->rnd = 0.25f; | |
| 607 | + c->srr = 0.25f; | |
| 608 | + tSampleReducer_init(&c->sReducer); | |
| 609 | + c->gain = (c->div / SCALAR) * 0.7f + 0.3f; | |
| 610 | +} | |
| 611 | + | |
| 612 | +void tCrusher_free (tCrusher* const cr) | |
| 613 | +{ | |
| 614 | + _tCrusher* c = *cr; | |
| 615 | + tSampleReducer_free(&c->sReducer); | |
| 616 | + leaf_free((char*)c); | |
| 617 | +} | |
| 618 | + | |
| 619 | +void tCrusher_initToPool (tCrusher* const cr, tMempool* const mp) | |
| 620 | +{ | |
| 621 | + _tMempool* m = *mp; | |
| 622 | + _tCrusher* c = *cr = (_tCrusher*) mpool_alloc(sizeof(_tCrusher), m); | |
| 623 | + | |
| 624 | + c->op = 4; | |
| 625 | + c->div = SCALAR; | |
| 626 | + c->rnd = 0.25f; | |
| 627 | + c->srr = 0.25f; | |
| 628 | + tSampleReducer_initToPool(&c->sReducer, mp); | |
| 629 | + c->gain = (c->div / SCALAR) * 0.7f + 0.3f; | |
| 630 | +} | |
| 631 | + | |
| 632 | +void tCrusher_freeFromPool (tCrusher* const cr, tMempool* const mp) | |
| 633 | +{ | |
| 634 | + _tMempool* m = *mp; | |
| 635 | + _tCrusher* c = *cr; | |
| 636 | + tSampleReducer_freeFromPool(&c->sReducer, mp); | |
| 637 | + mpool_free((char*)c, m); | |
| 638 | +} | |
| 639 | + | |
| 640 | +float tCrusher_tick (tCrusher* const cr, float input) | |
| 641 | +{ | |
| 642 | + _tCrusher* c = *cr; | |
| 643 | + | |
| 644 | + float sample = input; | |
| 645 | + | |
| 646 | + sample *= SCALAR; // SCALAR is 5000 by default | |
| 647 | + | |
| 648 | + sample = (int32_t) sample; | |
| 649 | + | |
| 650 | + sample /= c->div; | |
| 651 | + | |
| 652 | + sample = LEAF_bitwise_xor(sample, c->op << 23); | |
| 653 | + | |
| 654 | + sample = LEAF_clip(-1.f, sample, 1.f); | |
| 655 | + | |
| 656 | + sample = LEAF_round(sample, c->rnd); | |
| 657 | + | |
| 658 | + sample = tSampleReducer_tick(&c->sReducer, sample); | |
| 659 | + | |
| 660 | + return sample * c->gain; | |
| 661 | + | |
| 662 | +} | |
| 663 | + | |
| 664 | +void tCrusher_setOperation (tCrusher* const cr, float op) | |
| 665 | +{ | |
| 666 | + _tCrusher* c = *cr; | |
| 667 | + c->op = (uint32_t) (op * 8.0f); | |
| 668 | +} | |
| 669 | + | |
| 670 | +// 0.0 - 1.0 | |
| 671 | +void tCrusher_setQuality (tCrusher* const cr, float val) | |
| 672 | +{ | |
| 673 | + _tCrusher* c = *cr; | |
| 674 | + | |
| 675 | + val = LEAF_clip(0.0f, val, 1.0f); | |
| 676 | + | |
| 677 | + c->div = 0.01f + val * SCALAR; | |
| 678 | + | |
| 679 | + c->gain = (c->div / SCALAR) * 0.7f + 0.3f; | |
| 680 | +} | |
| 681 | + | |
| 682 | +// what decimal to round to | |
| 683 | +void tCrusher_setRound (tCrusher* const cr, float rnd) | |
| 684 | +{ | |
| 685 | + _tCrusher* c = *cr; | |
| 686 | + c->rnd = fabsf(rnd); | |
| 687 | +} | |
| 688 | + | |
| 689 | +void tCrusher_setSamplingRatio (tCrusher* const cr, float ratio) | |
| 690 | +{ | |
| 691 | + _tCrusher* c = *cr; | |
| 692 | + c->srr = ratio; | |
| 693 | + tSampleReducer_setRatio(&c->sReducer, ratio); | |
| 694 | + | |
| 695 | +} | |
| @@ -0,0 +1,281 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-dynamics.c | |
| 4 | + Created: 30 Nov 2018 11:56:49am | |
| 5 | + Author: airship | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-dynamics.h" | |
| 12 | + | |
| 13 | +#else | |
| 14 | + | |
| 15 | +#include "../Inc/leaf-dynamics.h" | |
| 16 | + | |
| 17 | +#endif | |
| 18 | + | |
| 19 | +//============================================================================== | |
| 20 | + | |
| 21 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Compressor ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 22 | + | |
| 23 | +/* | |
| 24 | + tCompressor* tCompressorInit(int tauAttack, int tauRelease) | |
| 25 | + { | |
| 26 | + tCompressor* c = &leaf.tCompressorRegistry[leaf.registryIndex[T_COMPRESSOR]++]; | |
| 27 | + | |
| 28 | + c->tauAttack = tauAttack; | |
| 29 | + c->tauRelease = tauRelease; | |
| 30 | + | |
| 31 | + c->x_G[0] = 0.0f, c->x_G[1] = 0.0f, | |
| 32 | + c->y_G[0] = 0.0f, c->y_G[1] = 0.0f, | |
| 33 | + c->x_T[0] = 0.0f, c->x_T[1] = 0.0f, | |
| 34 | + c->y_T[0] = 0.0f, c->y_T[1] = 0.0f; | |
| 35 | + | |
| 36 | + c->T = 0.0f; // Threshold | |
| 37 | + c->R = 1.0f; // compression Ratio | |
| 38 | + c->M = 0.0f; // decibel Make-up gain | |
| 39 | + c->W = 0.0f; // decibel Width of knee transition | |
| 40 | + | |
| 41 | + return c; | |
| 42 | + } | |
| 43 | + */ | |
| 44 | +void tCompressor_init(tCompressor* const comp) | |
| 45 | +{ | |
| 46 | + _tCompressor* c = *comp = (_tCompressor*) leaf_alloc(sizeof(_tCompressor)); | |
| 47 | + | |
| 48 | + c->tauAttack = 100; | |
| 49 | + c->tauRelease = 100; | |
| 50 | + | |
| 51 | + c->isActive = OFALSE; | |
| 52 | + | |
| 53 | + c->T = 0.0f; // Threshold | |
| 54 | + c->R = 0.5f; // compression Ratio | |
| 55 | + c->M = 3.0f; // decibel Width of knee transition | |
| 56 | + c->W = 1.0f; // decibel Make-up gain | |
| 57 | +} | |
| 58 | + | |
| 59 | +void tCompressor_free(tCompressor* const comp) | |
| 60 | +{ | |
| 61 | + _tCompressor* c = *comp; | |
| 62 | + | |
| 63 | + leaf_free((char*)c); | |
| 64 | +} | |
| 65 | + | |
| 66 | +void tCompressor_initToPool (tCompressor* const comp, tMempool* const mp) | |
| 67 | +{ | |
| 68 | + _tMempool* m = *mp; | |
| 69 | + _tCompressor* c = *comp = (_tCompressor*) mpool_alloc(sizeof(_tCompressor), m); | |
| 70 | + | |
| 71 | + c->tauAttack = 100; | |
| 72 | + c->tauRelease = 100; | |
| 73 | + | |
| 74 | + c->isActive = OFALSE; | |
| 75 | + | |
| 76 | + c->T = 0.0f; // Threshold | |
| 77 | + c->R = 0.5f; // compression Ratio | |
| 78 | + c->M = 3.0f; // decibel Width of knee transition | |
| 79 | + c->W = 1.0f; // decibel Make-up gain | |
| 80 | +} | |
| 81 | + | |
| 82 | +void tCompressor_freeFromPool(tCompressor* const comp, tMempool* const mp) | |
| 83 | +{ | |
| 84 | + _tMempool* m = *mp; | |
| 85 | + _tCompressor* c = *comp; | |
| 86 | + | |
| 87 | + mpool_free((char*)c, m); | |
| 88 | +} | |
| 89 | + | |
| 90 | +float tCompressor_tick(tCompressor* const comp, float in) | |
| 91 | +{ | |
| 92 | + _tCompressor* c = *comp; | |
| 93 | + | |
| 94 | + float slope, overshoot; | |
| 95 | + float alphaAtt, alphaRel; | |
| 96 | + | |
| 97 | + float in_db = 20.0f * log10f( fmaxf( fabsf( in), 0.000001f)), out_db = 0.0f; | |
| 98 | + | |
| 99 | + c->y_T[1] = c->y_T[0]; | |
| 100 | + | |
| 101 | + slope = c->R - 1.0f; // feed-forward topology; was 1/C->R - 1 | |
| 102 | + | |
| 103 | + overshoot = in_db - c->T; | |
| 104 | + | |
| 105 | + | |
| 106 | + if (overshoot <= -(c->W * 0.5f)) | |
| 107 | + { | |
| 108 | + out_db = in_db; | |
| 109 | + c->isActive = OFALSE; | |
| 110 | + } | |
| 111 | + else if ((overshoot > -(c->W * 0.5f)) && (overshoot < (c->W * 0.5f))) | |
| 112 | + { | |
| 113 | + out_db = in_db + slope * (powf((overshoot + c->W*0.5f),2) / (2.0f * c->W)); // .^ 2 ??? | |
| 114 | + c->isActive = OTRUE; | |
| 115 | + } | |
| 116 | + else if (overshoot >= (c->W * 0.5f)) | |
| 117 | + { | |
| 118 | + out_db = in_db + slope * overshoot; | |
| 119 | + c->isActive = OTRUE; | |
| 120 | + } | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + c->x_T[0] = out_db - in_db; | |
| 125 | + | |
| 126 | + alphaAtt = expf(-1.0f/(0.001f * c->tauAttack * leaf.sampleRate)); | |
| 127 | + alphaRel = expf(-1.0f/(0.001f * c->tauRelease * leaf.sampleRate)); | |
| 128 | + | |
| 129 | + if (c->x_T[0] > c->y_T[1]) | |
| 130 | + c->y_T[0] = alphaAtt * c->y_T[1] + (1-alphaAtt) * c->x_T[0]; | |
| 131 | + else | |
| 132 | + c->y_T[0] = alphaRel * c->y_T[1] + (1-alphaRel) * c->x_T[0]; | |
| 133 | + | |
| 134 | + float attenuation = powf(10.0f, ((c->M - c->y_T[0])/20.0f)); | |
| 135 | + | |
| 136 | + return attenuation * in; | |
| 137 | +} | |
| 138 | + | |
| 139 | +/* Feedback Leveler */ | |
| 140 | + | |
| 141 | +void tFeedbackLeveler_init(tFeedbackLeveler* const fb, float targetLevel, float factor, float strength, int mode) | |
| 142 | +{ | |
| 143 | + _tFeedbackLeveler* p = *fb = (_tFeedbackLeveler*) leaf_alloc(sizeof(_tFeedbackLeveler)); | |
| 144 | + | |
| 145 | + p->curr=0.0f; | |
| 146 | + p->targetLevel=targetLevel; | |
| 147 | + tPowerFollower_init(&p->pwrFlw,factor); | |
| 148 | + p->mode=mode; | |
| 149 | + p->strength=strength; | |
| 150 | +} | |
| 151 | + | |
| 152 | +void tFeedbackLeveler_free(tFeedbackLeveler* const fb) | |
| 153 | +{ | |
| 154 | + _tFeedbackLeveler* p = *fb; | |
| 155 | + | |
| 156 | + tPowerFollower_free(&p->pwrFlw); | |
| 157 | + leaf_free((char*)p); | |
| 158 | +} | |
| 159 | + | |
| 160 | +void tFeedbackLeveler_initToPool (tFeedbackLeveler* const fb, float targetLevel, float factor, float strength, int mode, tMempool* const mp) | |
| 161 | +{ | |
| 162 | + _tMempool* m = *mp; | |
| 163 | + _tFeedbackLeveler* p = *fb = (_tFeedbackLeveler*) mpool_alloc(sizeof(_tFeedbackLeveler), m); | |
| 164 | + | |
| 165 | + p->curr=0.0f; | |
| 166 | + p->targetLevel=targetLevel; | |
| 167 | + tPowerFollower_initToPool(&p->pwrFlw,factor, mp); | |
| 168 | + p->mode=mode; | |
| 169 | + p->strength=strength; | |
| 170 | +} | |
| 171 | + | |
| 172 | +void tFeedbackLeveler_freeFromPool (tFeedbackLeveler* const fb, tMempool* const mp) | |
| 173 | +{ | |
| 174 | + _tMempool* m = *mp; | |
| 175 | + _tFeedbackLeveler* p = *fb; | |
| 176 | + | |
| 177 | + tPowerFollower_freeFromPool(&p->pwrFlw, mp); | |
| 178 | + mpool_free((char*)p, m); | |
| 179 | +} | |
| 180 | + | |
| 181 | +void tFeedbackLeveler_setStrength(tFeedbackLeveler* const fb, float strength) | |
| 182 | +{ // strength is how strongly level diff is affecting the amp ratio | |
| 183 | + // try 0.125 for a start | |
| 184 | + _tFeedbackLeveler* p = *fb; | |
| 185 | + p->strength=strength; | |
| 186 | +} | |
| 187 | + | |
| 188 | +void tFeedbackLeveler_setFactor(tFeedbackLeveler* const fb, float factor) | |
| 189 | +{ | |
| 190 | + _tFeedbackLeveler* p = *fb; | |
| 191 | + tPowerFollower_setFactor(&p->pwrFlw,factor); | |
| 192 | +} | |
| 193 | + | |
| 194 | +void tFeedbackLeveler_setMode(tFeedbackLeveler* const fb, int mode) | |
| 195 | +{ // 0 for decaying with upwards lev limiting, 1 for constrained absolute level (also downwards limiting) | |
| 196 | + _tFeedbackLeveler* p = *fb; | |
| 197 | + p->mode=mode; | |
| 198 | +} | |
| 199 | + | |
| 200 | +float tFeedbackLeveler_tick(tFeedbackLeveler* const fb, float input) | |
| 201 | +{ | |
| 202 | + _tFeedbackLeveler* p = *fb; | |
| 203 | + float levdiff=(tPowerFollower_tick(&p->pwrFlw, input)-p->targetLevel); | |
| 204 | + if (p->mode==0 && levdiff<0.0f) levdiff=0.0f; | |
| 205 | + p->curr=input*(1.0f-p->strength*levdiff); | |
| 206 | + return p->curr; | |
| 207 | +} | |
| 208 | + | |
| 209 | +float tFeedbackLeveler_sample(tFeedbackLeveler* const fb) | |
| 210 | +{ | |
| 211 | + _tFeedbackLeveler* p = *fb; | |
| 212 | + return p->curr; | |
| 213 | +} | |
| 214 | + | |
| 215 | + | |
| 216 | +void tFeedbackLeveler_setTargetLevel (tFeedbackLeveler* const fb, float TargetLevel) | |
| 217 | +{ | |
| 218 | + _tFeedbackLeveler* p = *fb; | |
| 219 | + p->targetLevel=TargetLevel; | |
| 220 | +} | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | +void tThreshold_init(tThreshold* const th, float low, float high) | |
| 225 | +{ | |
| 226 | + tThreshold_initToPool(th, low, high, &leaf.mempool); | |
| 227 | +} | |
| 228 | + | |
| 229 | +void tThreshold_free(tThreshold* const th) | |
| 230 | +{ | |
| 231 | + tThreshold_freeFromPool(th, &leaf.mempool); | |
| 232 | +} | |
| 233 | + | |
| 234 | +void tThreshold_initToPool (tThreshold* const th, float low, float high, tMempool* const mp) | |
| 235 | +{ | |
| 236 | + _tMempool* m = *mp; | |
| 237 | + _tThreshold* t = *th = (_tThreshold*) mpool_alloc(sizeof(_tThreshold), m); | |
| 238 | + | |
| 239 | + t->highThresh = high; | |
| 240 | + t->lowThresh = low; | |
| 241 | + | |
| 242 | + t->currentValue = 0; | |
| 243 | +} | |
| 244 | + | |
| 245 | +void tThreshold_freeFromPool(tThreshold* const th, tMempool* const mp) | |
| 246 | +{ | |
| 247 | + _tMempool* m = *mp; | |
| 248 | + _tThreshold* t = *th; | |
| 249 | + | |
| 250 | + mpool_free((char*)t, m); | |
| 251 | +} | |
| 252 | + | |
| 253 | +int tThreshold_tick(tThreshold* const th, float in) | |
| 254 | +{ | |
| 255 | + _tThreshold* t = *th; | |
| 256 | + | |
| 257 | + if (in >= t->highThresh) | |
| 258 | + { | |
| 259 | + t->currentValue = 1; | |
| 260 | + } | |
| 261 | + else if (in <= t->lowThresh) | |
| 262 | + { | |
| 263 | + t->currentValue = 0; | |
| 264 | + } | |
| 265 | + | |
| 266 | + return t->currentValue; | |
| 267 | +} | |
| 268 | + | |
| 269 | +void tThreshold_setLow(tThreshold* const th, float low) | |
| 270 | +{ | |
| 271 | + _tThreshold* t = *th; | |
| 272 | + | |
| 273 | + t->lowThresh = low; | |
| 274 | +} | |
| 275 | + | |
| 276 | +void tThreshold_setHigh(tThreshold* const th, float high) | |
| 277 | +{ | |
| 278 | + _tThreshold* t = *th; | |
| 279 | + | |
| 280 | + t->highThresh = high; | |
| 281 | +} | |
| @@ -0,0 +1,1853 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-vocoder.c | |
| 4 | + Created: 20 Jan 2017 12:01:54pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-effects.c" | |
| 12 | +#include "..\leaf.h" | |
| 13 | + | |
| 14 | +#else | |
| 15 | + | |
| 16 | +#include "../Inc/leaf-effects.h" | |
| 17 | +#include "../leaf.h" | |
| 18 | + | |
| 19 | +#endif | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | +//============================================================================================================ | |
| 24 | +// TALKBOX | |
| 25 | +//============================================================================================================ | |
| 26 | + | |
| 27 | +//LPC vocoder adapted from MDA's excellent open source talkbox plugin code | |
| 28 | +void tTalkbox_init(tTalkbox* const voc, int bufsize) | |
| 29 | +{ | |
| 30 | + | |
| 31 | + _tTalkbox* v = *voc = (_tTalkbox*) leaf_alloc(sizeof(_tTalkbox)); | |
| 32 | + | |
| 33 | + v->param[0] = 0.5f; //wet | |
| 34 | + v->param[1] = 0.0f; //dry | |
| 35 | + v->param[2] = 0; // Swap | |
| 36 | + v->param[3] = 1.0f; //quality | |
| 37 | + v->warpFactor = 0.0f; | |
| 38 | + v->warpOn = 0; | |
| 39 | + v->bufsize = bufsize; | |
| 40 | + | |
| 41 | + v->car0 = (float*) leaf_alloc(sizeof(float) * v->bufsize); | |
| 42 | + v->car1 = (float*) leaf_alloc(sizeof(float) * v->bufsize); | |
| 43 | + v->window = (float*) leaf_alloc(sizeof(float) * v->bufsize); | |
| 44 | + v->buf0 = (float*) leaf_alloc(sizeof(float) * v->bufsize); | |
| 45 | + v->buf1 = (float*) leaf_alloc(sizeof(float) * v->bufsize); | |
| 46 | + | |
| 47 | + tTalkbox_update(voc); | |
| 48 | + tTalkbox_suspend(voc); | |
| 49 | +} | |
| 50 | + | |
| 51 | +void tTalkbox_free(tTalkbox* const voc) | |
| 52 | +{ | |
| 53 | + _tTalkbox* v = *voc; | |
| 54 | + | |
| 55 | + leaf_free((char*)v->buf1); | |
| 56 | + leaf_free((char*)v->buf0); | |
| 57 | + leaf_free((char*)v->window); | |
| 58 | + leaf_free((char*)v->car1); | |
| 59 | + leaf_free((char*)v->car0); | |
| 60 | + | |
| 61 | + leaf_free((char*)v); | |
| 62 | +} | |
| 63 | + | |
| 64 | +void tTalkbox_initToPool (tTalkbox* const voc, int bufsize, tMempool* const mp) | |
| 65 | +{ | |
| 66 | + _tMempool* m = *mp; | |
| 67 | + _tTalkbox* v = *voc = (_tTalkbox*) mpool_alloc(sizeof(_tTalkbox), m); | |
| 68 | + | |
| 69 | + v->param[0] = 0.5f; //wet | |
| 70 | + v->param[1] = 0.0f; //dry | |
| 71 | + v->param[2] = 0; // Swap | |
| 72 | + v->param[3] = 1.0f; //quality | |
| 73 | + v->warpFactor = 0.0f; | |
| 74 | + v->warpOn = 0; | |
| 75 | + v->bufsize = bufsize; | |
| 76 | + | |
| 77 | + v->car0 = (float*) mpool_alloc(sizeof(float) * v->bufsize, m); | |
| 78 | + v->car1 = (float*) mpool_alloc(sizeof(float) * v->bufsize, m); | |
| 79 | + v->window = (float*) mpool_alloc(sizeof(float) * v->bufsize, m); | |
| 80 | + v->buf0 = (float*) mpool_alloc(sizeof(float) * v->bufsize, m); | |
| 81 | + v->buf1 = (float*) mpool_alloc(sizeof(float) * v->bufsize, m); | |
| 82 | + | |
| 83 | + tTalkbox_update(voc); | |
| 84 | + tTalkbox_suspend(voc); | |
| 85 | +} | |
| 86 | + | |
| 87 | +void tTalkbox_freeFromPool (tTalkbox* const voc, tMempool* const mp) | |
| 88 | +{ | |
| 89 | + _tMempool* m = *mp; | |
| 90 | + _tTalkbox* v = *voc; | |
| 91 | + | |
| 92 | + mpool_free((char*)v->buf1, m); | |
| 93 | + mpool_free((char*)v->buf0, m); | |
| 94 | + mpool_free((char*)v->window, m); | |
| 95 | + mpool_free((char*)v->car1, m); | |
| 96 | + mpool_free((char*)v->car0, m); | |
| 97 | + | |
| 98 | + mpool_free((char*)v, m); | |
| 99 | +} | |
| 100 | + | |
| 101 | +void tTalkbox_update(tTalkbox* const voc) ///update internal parameters... | |
| 102 | +{ | |
| 103 | + _tTalkbox* v = *voc; | |
| 104 | + | |
| 105 | + float fs = leaf.sampleRate; | |
| 106 | + if(fs < 8000.0f) fs = 8000.0f; | |
| 107 | + if(fs > 96000.0f) fs = 96000.0f; | |
| 108 | + | |
| 109 | + int32_t n = (int32_t)(0.01633f * fs); //this sets the window time to 16ms if the buffer is large enough. Buffer needs to be at least 784 samples at 48000 | |
| 110 | + if(n > v->bufsize) n = v->bufsize; | |
| 111 | + | |
| 112 | + //O = (VstInt32)(0.0005f * fs); | |
| 113 | + v->O = (int32_t)((0.0001f + 0.0004f * v->param[3]) * fs); | |
| 114 | + | |
| 115 | + if(n != v->N) //recalc hanning window | |
| 116 | + { | |
| 117 | + v->N = n; | |
| 118 | + float dp = TWO_PI / v->N; | |
| 119 | + float p = 0.0f; | |
| 120 | + for(n=0; n<v->N; n++) | |
| 121 | + { | |
| 122 | + v->window[n] = 0.5f - 0.5f * cosf(p); | |
| 123 | + p += dp; | |
| 124 | + } | |
| 125 | + } | |
| 126 | + v->wet = 0.5f * v->param[0] * v->param[0]; | |
| 127 | + v->dry = 2.0f * v->param[1] * v->param[1]; | |
| 128 | +} | |
| 129 | + | |
| 130 | +void tTalkbox_suspend(tTalkbox* const voc) ///clear any buffers... | |
| 131 | +{ | |
| 132 | + _tTalkbox* v = *voc; | |
| 133 | + | |
| 134 | + v->pos = v->K = 0; | |
| 135 | + v->emphasis = 0.0f; | |
| 136 | + v->FX = 0; | |
| 137 | + | |
| 138 | + v->u0 = v->u1 = v->u2 = v->u3 = v->u4 = 0.0f; | |
| 139 | + v->d0 = v->d1 = v->d2 = v->d3 = v->d4 = 0.0f; | |
| 140 | + | |
| 141 | + for (int32_t i = 0; i < v->bufsize; i++) | |
| 142 | + { | |
| 143 | + v->buf0[i] = 0; | |
| 144 | + v->buf1[i] = 0; | |
| 145 | + v->car0[i] = 0; | |
| 146 | + v->car1[i] = 0; | |
| 147 | + } | |
| 148 | +} | |
| 149 | + | |
| 150 | +// warped autocorrelation adapted from ten.enegatum@liam's post on music-dsp 2004-04-07 09:37:51 | |
| 151 | +//find the order-P autocorrelation array, R, for the sequence x of length L and warping of lambda | |
| 152 | +//wAutocorrelate(&pfSrc[stIndex],siglen,R,P,0); | |
| 153 | +void tTalkbox_warpedAutocorrelate(float * x, unsigned int L, float * R, unsigned int P, float lambda) | |
| 154 | +{ | |
| 155 | + double dl[L]; | |
| 156 | + double Rt[L]; | |
| 157 | + double r1,r2,r1t; | |
| 158 | + R[0]=0; | |
| 159 | + Rt[0]=0; | |
| 160 | + r1=0; | |
| 161 | + r2=0; | |
| 162 | + r1t=0; | |
| 163 | + for(int32_t k=0; k<L;k++) | |
| 164 | + { | |
| 165 | + Rt[0] += (double)(x[k]) * (double)(x[k]); | |
| 166 | + | |
| 167 | + dl[k]= r1 - (double)(lambda) * (double)(x[k]-r2); | |
| 168 | + r1 = x[k]; | |
| 169 | + r2 = dl[k]; | |
| 170 | + } | |
| 171 | + for(int32_t i=1; i<=P; i++) | |
| 172 | + { | |
| 173 | + Rt[i]=0; | |
| 174 | + r1=0; | |
| 175 | + r2=0; | |
| 176 | + for(unsigned int k=0; k<L;k++) | |
| 177 | + { | |
| 178 | + Rt[i] += (double) (dl[k]) * (double)(x[k]); | |
| 179 | + | |
| 180 | + r1t = dl[k]; | |
| 181 | + dl[k]= r1 - (double)(lambda) * (double)(r1t-r2); | |
| 182 | + r1 = r1t; | |
| 183 | + r2 = dl[k]; | |
| 184 | + } | |
| 185 | + } | |
| 186 | + for(int32_t i=0; i<=P; i++) | |
| 187 | + { | |
| 188 | + R[i]=(float)(Rt[i]); | |
| 189 | + } | |
| 190 | + | |
| 191 | +} | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | +#define ORD_MAX 35 // Was 100. | |
| 196 | +// order is defined by the set_quality function. | |
| 197 | +// it's set to max out at 0.0005 of sample rate (if you don't go above 1.0f in the quality setting) == at 48000 that's 24. | |
| 198 | +// -JS | |
| 199 | +void tTalkbox_lpc(float *buf, float *car, int32_t n, int32_t o, float warp, int warpOn) | |
| 200 | +{ | |
| 201 | + float z[ORD_MAX], r[ORD_MAX], k[ORD_MAX], G, x; | |
| 202 | + int32_t i, j, nn=n; | |
| 203 | + | |
| 204 | + if (warpOn == 0) | |
| 205 | + { | |
| 206 | + for(j=0; j<=o; j++, nn--) //buf[] is already emphasized and windowed | |
| 207 | + { | |
| 208 | + z[j] = r[j] = 0.0f; | |
| 209 | + for(i=0; i<nn; i++) r[j] += buf[i] * buf[i+j]; //autocorrelation | |
| 210 | + } | |
| 211 | + } | |
| 212 | + else | |
| 213 | + { | |
| 214 | + for(j=0; j<=o; j++, nn--) //buf[] is already emphasized and windowed | |
| 215 | + { | |
| 216 | + z[j] = r[j] = 0.0f; | |
| 217 | + } | |
| 218 | + tTalkbox_warpedAutocorrelate(buf, n, r, o, warp); | |
| 219 | + } | |
| 220 | + | |
| 221 | + r[0] *= 1.001f; //stability fix | |
| 222 | + | |
| 223 | + float min = 0.00001f; | |
| 224 | + if(r[0] < min) { for(i=0; i<n; i++) buf[i] = 0.0f; return; } | |
| 225 | + | |
| 226 | + tTalkbox_lpcDurbin(r, o, k, &G); //calc reflection coeffs | |
| 227 | + | |
| 228 | + //this is for stability to keep reflection coefficients inside the unit circle | |
| 229 | + //but in Harma's papers I've seen 0.998. just needs to be less than 1 it seems but maybe some wiggle room to avoid instability from floating point precision -JS | |
| 230 | + for(i=0; i<=o; i++) | |
| 231 | + { | |
| 232 | + if(k[i] > 0.995f) k[i] = 0.995f; else if(k[i] < -0.995f) k[i] = -.995f; | |
| 233 | + } | |
| 234 | + | |
| 235 | + for(i=0; i<n; i++) | |
| 236 | + { | |
| 237 | + x = G * car[i]; | |
| 238 | + for(j=o; j>0; j--) //lattice filter | |
| 239 | + { | |
| 240 | + x -= k[j] * z[j-1]; | |
| 241 | + z[j] = z[j-1] + k[j] * x; | |
| 242 | + } | |
| 243 | + buf[i] = z[0] = x; //output buf[] will be windowed elsewhere | |
| 244 | + } | |
| 245 | +} | |
| 246 | + | |
| 247 | + | |
| 248 | +void tTalkbox_lpcDurbin(float *r, int p, float *k, float *g) | |
| 249 | +{ | |
| 250 | + int i, j; | |
| 251 | + float a[ORD_MAX], at[ORD_MAX], e=r[0]; | |
| 252 | + | |
| 253 | + for(i=0; i<=p; i++) a[i] = 0.0f; //probably don't need to clear at[] or k[] | |
| 254 | + at[0] = 0.0f; | |
| 255 | + for(i=1; i<=p; i++) | |
| 256 | + { | |
| 257 | + k[i] = -r[i]; | |
| 258 | + | |
| 259 | + for(j=1; j<i; j++) | |
| 260 | + { | |
| 261 | + at[j] = a[j]; | |
| 262 | + k[i] -= a[j] * r[i-j]; | |
| 263 | + } | |
| 264 | + if(fabs(e) < 1.0e-20f) { e = 0.0f; break; } | |
| 265 | + k[i] /= e; | |
| 266 | + | |
| 267 | + a[i] = k[i]; | |
| 268 | + for(j=1; j<i; j++) a[j] = at[j] + k[i] * at[i-j]; | |
| 269 | + | |
| 270 | + e *= 1.0f - k[i] * k[i]; | |
| 271 | + } | |
| 272 | + | |
| 273 | + if(e < 1.0e-20f) e = 0.0f; | |
| 274 | + *g = sqrtf(e); | |
| 275 | +} | |
| 276 | + | |
| 277 | +float tTalkbox_tick(tTalkbox* const voc, float synth, float voice) | |
| 278 | +{ | |
| 279 | + _tTalkbox* v = *voc; | |
| 280 | + | |
| 281 | + int32_t p0=v->pos, p1 = (v->pos + v->N/2) % v->N; | |
| 282 | + float e=v->emphasis, w, o, x, fx=v->FX; | |
| 283 | + float p, q, h0=0.3f, h1=0.77f; | |
| 284 | + | |
| 285 | + o = voice; | |
| 286 | + x = synth; | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + p = v->d0 + h0 * x; v->d0 = v->d1; v->d1 = x - h0 * p; | |
| 291 | + q = v->d2 + h1 * v->d4; v->d2 = v->d3; v->d3 = v->d4 - h1 * q; | |
| 292 | + v->d4 = x; | |
| 293 | + x = p + q; | |
| 294 | + | |
| 295 | + if(v->K++) | |
| 296 | + { | |
| 297 | + v->K = 0; | |
| 298 | + | |
| 299 | + v->car0[p0] = v->car1[p1] = x; //carrier input | |
| 300 | + | |
| 301 | + x = o - e; e = o; //6dB/oct pre-emphasis | |
| 302 | + | |
| 303 | + w = v->window[p0]; fx = v->buf0[p0] * w; v->buf0[p0] = x * w; //50% overlapping hanning windows | |
| 304 | + if(++p0 >= v->N) { tTalkbox_lpc(v->buf0, v->car0, v->N, v->O, v->warpFactor, v->warpOn); p0 = 0; } | |
| 305 | + | |
| 306 | + w = 1.0f - w; fx += v->buf1[p1] * w; v->buf1[p1] = x * w; | |
| 307 | + if(++p1 >= v->N) { tTalkbox_lpc(v->buf1, v->car1, v->N, v->O, v->warpFactor, v->warpOn); p1 = 0; } | |
| 308 | + } | |
| 309 | + | |
| 310 | + p = v->u0 + h0 * fx; v->u0 = v->u1; v->u1 = fx - h0 * p; | |
| 311 | + q = v->u2 + h1 * v->u4; v->u2 = v->u3; v->u3 = v->u4 - h1 * q; | |
| 312 | + v->u4 = fx; | |
| 313 | + x = p + q; | |
| 314 | + | |
| 315 | + o = x; | |
| 316 | + | |
| 317 | + v->emphasis = e; | |
| 318 | + v->pos = p0; | |
| 319 | + v->FX = fx; | |
| 320 | + | |
| 321 | + | |
| 322 | + return o; | |
| 323 | +} | |
| 324 | + | |
| 325 | +void tTalkbox_setQuality(tTalkbox* const voc, float quality) | |
| 326 | +{ | |
| 327 | + _tTalkbox* v = *voc; | |
| 328 | + | |
| 329 | + v->param[3] = quality; | |
| 330 | + v->O = (int32_t)((0.0001f + 0.0004f * v->param[3]) * leaf.sampleRate); | |
| 331 | + if (v->O >= ORD_MAX) | |
| 332 | + { | |
| 333 | + v->O = ORD_MAX-1; | |
| 334 | + } | |
| 335 | +} | |
| 336 | + | |
| 337 | +void tTalkbox_setWarpFactor(tTalkbox* const voc, float warpFactor) | |
| 338 | +{ | |
| 339 | + _tTalkbox* v = *voc; | |
| 340 | + | |
| 341 | + v->warpFactor = warpFactor; | |
| 342 | +} | |
| 343 | + | |
| 344 | +void tTalkbox_setWarpOn(tTalkbox* const voc, float warpOn) | |
| 345 | +{ | |
| 346 | + _tTalkbox* v = *voc; | |
| 347 | + | |
| 348 | + v->warpOn = warpOn; | |
| 349 | +} | |
| 350 | + | |
| 351 | + | |
| 352 | +//============================================================================================================ | |
| 353 | +// VOCODER | |
| 354 | +//============================================================================================================ | |
| 355 | + | |
| 356 | +void tVocoder_init (tVocoder* const voc) | |
| 357 | +{ | |
| 358 | + _tVocoder* v = *voc = (_tVocoder*) leaf_alloc(sizeof(_tVocoder)); | |
| 359 | + | |
| 360 | + v->param[0] = 0.33f; //input select | |
| 361 | + v->param[1] = 0.50f; //output dB | |
| 362 | + v->param[2] = 0.40f; //hi thru | |
| 363 | + v->param[3] = 0.40f; //hi band | |
| 364 | + v->param[4] = 0.16f; //envelope | |
| 365 | + v->param[5] = 0.55f; //filter q | |
| 366 | + v->param[6] = 0.6667f;//freq range | |
| 367 | + v->param[7] = 0.33f; //num bands | |
| 368 | + | |
| 369 | + tVocoder_update(voc); | |
| 370 | +} | |
| 371 | + | |
| 372 | +void tVocoder_free (tVocoder* const voc) | |
| 373 | +{ | |
| 374 | + _tVocoder* v = *voc; | |
| 375 | + | |
| 376 | + leaf_free((char*)v); | |
| 377 | +} | |
| 378 | + | |
| 379 | +void tVocoder_initToPool (tVocoder* const voc, tMempool* const mp) | |
| 380 | +{ | |
| 381 | + _tMempool* m = *mp; | |
| 382 | + _tVocoder* v = *voc = (_tVocoder*) mpool_alloc(sizeof(_tVocoder), m); | |
| 383 | + | |
| 384 | + v->param[0] = 0.33f; //input select | |
| 385 | + v->param[1] = 0.50f; //output dB | |
| 386 | + v->param[2] = 0.40f; //hi thru | |
| 387 | + v->param[3] = 0.40f; //hi band | |
| 388 | + v->param[4] = 0.16f; //envelope | |
| 389 | + v->param[5] = 0.55f; //filter q | |
| 390 | + v->param[6] = 0.6667f;//freq range | |
| 391 | + v->param[7] = 0.33f; //num bands | |
| 392 | + | |
| 393 | + tVocoder_update(voc); | |
| 394 | +} | |
| 395 | + | |
| 396 | +void tVocoder_freeFromPool (tVocoder* const voc, tMempool* const mp) | |
| 397 | +{ | |
| 398 | + _tMempool* m = *mp; | |
| 399 | + _tVocoder* v = *voc; | |
| 400 | + | |
| 401 | + mpool_free((char*)v, m); | |
| 402 | +} | |
| 403 | + | |
| 404 | +void tVocoder_update (tVocoder* const voc) | |
| 405 | +{ | |
| 406 | + _tVocoder* v = *voc; | |
| 407 | + | |
| 408 | + float tpofs = 6.2831853f * leaf.invSampleRate; | |
| 409 | + | |
| 410 | + float rr, th, re; | |
| 411 | + | |
| 412 | + float sh; | |
| 413 | + | |
| 414 | + int32_t i; | |
| 415 | + | |
| 416 | + v->gain = (float)pow(10.0f, 2.0f * v->param[1] - 3.0f * v->param[5] - 2.0f); | |
| 417 | + | |
| 418 | + v->thru = (float)pow(10.0f, 0.5f + 2.0f * v->param[1]); | |
| 419 | + v->high = v->param[3] * v->param[3] * v->param[3] * v->thru; | |
| 420 | + v->thru *= v->param[2] * v->param[2] * v->param[2]; | |
| 421 | + | |
| 422 | + if(v->param[7]<0.5f) | |
| 423 | + { | |
| 424 | + v->nbnd=8; | |
| 425 | + re=0.003f; | |
| 426 | + v->f[1][2] = 3000.0f; | |
| 427 | + v->f[2][2] = 2200.0f; | |
| 428 | + v->f[3][2] = 1500.0f; | |
| 429 | + v->f[4][2] = 1080.0f; | |
| 430 | + v->f[5][2] = 700.0f; | |
| 431 | + v->f[6][2] = 390.0f; | |
| 432 | + v->f[7][2] = 190.0f; | |
| 433 | + } | |
| 434 | + else | |
| 435 | + { | |
| 436 | + v->nbnd=16; | |
| 437 | + re=0.0015f; | |
| 438 | + v->f[ 1][2] = 5000.0f; //+1000 | |
| 439 | + v->f[ 2][2] = 4000.0f; //+750 | |
| 440 | + v->f[ 3][2] = 3250.0f; //+500 | |
| 441 | + v->f[ 4][2] = 2750.0f; //+450 | |
| 442 | + v->f[ 5][2] = 2300.0f; //+300 | |
| 443 | + v->f[ 6][2] = 2000.0f; //+250 | |
| 444 | + v->f[ 7][2] = 1750.0f; //+250 | |
| 445 | + v->f[ 8][2] = 1500.0f; //+250 | |
| 446 | + v->f[ 9][2] = 1250.0f; //+250 | |
| 447 | + v->f[10][2] = 1000.0f; //+250 | |
| 448 | + v->f[11][2] = 750.0f; //+210 | |
| 449 | + v->f[12][2] = 540.0f; //+190 | |
| 450 | + v->f[13][2] = 350.0f; //+155 | |
| 451 | + v->f[14][2] = 195.0f; //+100 | |
| 452 | + v->f[15][2] = 95.0f; | |
| 453 | + } | |
| 454 | + | |
| 455 | + if(v->param[4]<0.05f) //freeze | |
| 456 | + { | |
| 457 | + for(i=0;i<v->nbnd;i++) v->f[i][12]=0.0f; | |
| 458 | + } | |
| 459 | + else | |
| 460 | + { | |
| 461 | + v->f[0][12] = (float)pow(10.0, -1.7 - 2.7f * v->param[4]); //envelope speed | |
| 462 | + | |
| 463 | + rr = 0.022f / (float)v->nbnd; //minimum proportional to frequency to stop distortion | |
| 464 | + for(i=1;i<v->nbnd;i++) | |
| 465 | + { | |
| 466 | + v->f[i][12] = (float)(0.025 - rr * (double)i); | |
| 467 | + if(v->f[0][12] < v->f[i][12]) v->f[i][12] = v->f[0][12]; | |
| 468 | + } | |
| 469 | + v->f[0][12] = 0.5f * v->f[0][12]; //only top band is at full rate | |
| 470 | + } | |
| 471 | + | |
| 472 | + rr = 1.0 - pow(10.0f, -1.0f - 1.2f * v->param[5]); | |
| 473 | + sh = (float)pow(2.0f, 3.0f * v->param[6] - 1.0f); //filter bank range shift | |
| 474 | + | |
| 475 | + for(i=1;i<v->nbnd;i++) | |
| 476 | + { | |
| 477 | + v->f[i][2] *= sh; | |
| 478 | + th = acos((2.0 * rr * cos(tpofs * v->f[i][2])) / (1.0 + rr * rr)); | |
| 479 | + v->f[i][0] = (float)(2.0 * rr * cos(th)); //a0 | |
| 480 | + v->f[i][1] = (float)(-rr * rr); //a1 | |
| 481 | + //was .98 | |
| 482 | + v->f[i][2] *= 0.96f; //shift 2nd stage slightly to stop high resonance peaks | |
| 483 | + th = acos((2.0 * rr * cos(tpofs * v->f[i][2])) / (1.0 + rr * rr)); | |
| 484 | + v->f[i][2] = (float)(2.0 * rr * cos(th)); | |
| 485 | + } | |
| 486 | +} | |
| 487 | + | |
| 488 | +float tVocoder_tick (tVocoder* const voc, float synth, float voice) | |
| 489 | +{ | |
| 490 | + _tVocoder* v = *voc; | |
| 491 | + | |
| 492 | + float a, b, o=0.0f, aa, bb, oo = v->kout, g = v->gain, ht = v->thru, hh = v->high, tmp; | |
| 493 | + uint32_t i, k = v->kval, nb = v->nbnd; | |
| 494 | + | |
| 495 | + a = voice; //speech | |
| 496 | + b = synth; //synth | |
| 497 | + | |
| 498 | + tmp = a - v->f[0][7]; //integrate modulator for HF band and filter bank pre-emphasis | |
| 499 | + v->f[0][7] = a; | |
| 500 | + a = tmp; | |
| 501 | + | |
| 502 | + if(tmp<0.0f) tmp = -tmp; | |
| 503 | + v->f[0][11] -= v->f[0][12] * (v->f[0][11] - tmp); //high band envelope | |
| 504 | + o = v->f[0][11] * (ht * a + hh * (b - v->f[0][3])); //high band + high thru | |
| 505 | + | |
| 506 | + v->f[0][3] = b; //integrate carrier for HF band | |
| 507 | + | |
| 508 | + if(++k & 0x1) //this block runs at half sample rate | |
| 509 | + { | |
| 510 | + oo = 0.0f; | |
| 511 | + aa = a + v->f[0][9] - v->f[0][8] - v->f[0][8]; //apply zeros here instead of in each reson | |
| 512 | + v->f[0][9] = v->f[0][8]; v->f[0][8] = a; | |
| 513 | + bb = b + v->f[0][5] - v->f[0][4] - v->f[0][4]; | |
| 514 | + v->f[0][5] = v->f[0][4]; v->f[0][4] = b; | |
| 515 | + | |
| 516 | + for(i=1; i<nb; i++) //filter bank: 4th-order band pass | |
| 517 | + { | |
| 518 | + tmp = v->f[i][0] * v->f[i][3] + v->f[i][1] * v->f[i][4] + bb; | |
| 519 | + v->f[i][4] = v->f[i][3]; | |
| 520 | + v->f[i][3] = tmp; | |
| 521 | + tmp += v->f[i][2] * v->f[i][5] + v->f[i][1] * v->f[i][6]; | |
| 522 | + v->f[i][6] = v->f[i][5]; | |
| 523 | + v->f[i][5] = tmp; | |
| 524 | + | |
| 525 | + tmp = v->f[i][0] * v->f[i][7] + v->f[i][1] * v->f[i][8] + aa; | |
| 526 | + v->f[i][8] = v->f[i][7]; | |
| 527 | + v->f[i][7] = tmp; | |
| 528 | + tmp += v->f[i][2] * v->f[i][9] + v->f[i][1] * v->f[i][10]; | |
| 529 | + v->f[i][10] = v->f[i][9]; | |
| 530 | + v->f[i][9] = tmp; | |
| 531 | + | |
| 532 | + if(tmp<0.0f) tmp = -tmp; | |
| 533 | + v->f[i][11] -= v->f[i][12] * (v->f[i][11] - tmp); | |
| 534 | + oo += v->f[i][5] * v->f[i][11]; | |
| 535 | + } | |
| 536 | + } | |
| 537 | + o += oo * g; //effect of interpolating back up to Fs would be minimal (aliasing >16kHz) | |
| 538 | + | |
| 539 | + v->kout = oo; | |
| 540 | + v->kval = k & 0x1; | |
| 541 | + if(fabs(v->f[0][11])<1.0e-10) v->f[0][11] = 0.0f; //catch HF envelope denormal | |
| 542 | + | |
| 543 | + for(i=1;i<nb;i++) | |
| 544 | + if(fabs(v->f[i][3])<1.0e-10 || fabs(v->f[i][7])<1.0e-10) | |
| 545 | + for(k=3; k<12; k++) v->f[i][k] = 0.0f; //catch reson & envelope denormals | |
| 546 | + | |
| 547 | + if(fabs(o)>10.0f) tVocoder_suspend(voc); //catch instability | |
| 548 | + | |
| 549 | + return o; | |
| 550 | + | |
| 551 | +} | |
| 552 | + | |
| 553 | +void tVocoder_suspend (tVocoder* const voc) | |
| 554 | +{ | |
| 555 | + _tVocoder* v = *voc; | |
| 556 | + | |
| 557 | + int32_t i, j; | |
| 558 | + | |
| 559 | + for(i=0; i<v->nbnd; i++) for(j=3; j<12; j++) v->f[i][j] = 0.0f; //zero band filters and envelopes | |
| 560 | + v->kout = 0.0f; | |
| 561 | + v->kval = 0; | |
| 562 | +} | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | +/// Glottal Pulse (Rosenberg model) | |
| 567 | + | |
| 568 | +void tRosenbergGlottalPulse_init (tRosenbergGlottalPulse* const gp) | |
| 569 | +{ | |
| 570 | + tRosenbergGlottalPulse_initToPool(gp, &leaf.mempool); | |
| 571 | +} | |
| 572 | +void tRosenbergGlottalPulse_free (tRosenbergGlottalPulse* const gp) | |
| 573 | +{ | |
| 574 | + tRosenbergGlottalPulse_freeFromPool(gp, &leaf.mempool); | |
| 575 | +} | |
| 576 | +void tRosenbergGlottalPulse_initToPool (tRosenbergGlottalPulse* const gp, tMempool* const mp) | |
| 577 | +{ | |
| 578 | + | |
| 579 | + _tMempool* m = *mp; | |
| 580 | + _tRosenbergGlottalPulse* g = *gp = (_tRosenbergGlottalPulse*) mpool_alloc(sizeof(_tRosenbergGlottalPulse), m); | |
| 581 | + | |
| 582 | + g->phase = 0.0f; | |
| 583 | + g->openLength = 0.0f; | |
| 584 | + g->pulseLength = 0.0f; | |
| 585 | + g->freq = 0.0f; | |
| 586 | + g->inc = 0.0f; | |
| 587 | + | |
| 588 | + | |
| 589 | +} | |
| 590 | +void tRosenbergGlottalPulse_freeFromPool (tRosenbergGlottalPulse* const gp, tMempool* const mp) | |
| 591 | +{ | |
| 592 | + _tMempool* m = *mp; | |
| 593 | + _tRosenbergGlottalPulse* g = *gp; | |
| 594 | + mpool_free((char*)g, m); | |
| 595 | +} | |
| 596 | + | |
| 597 | +float tRosenbergGlottalPulse_tick (tRosenbergGlottalPulse* const gp) | |
| 598 | +{ | |
| 599 | + _tRosenbergGlottalPulse* g = *gp; | |
| 600 | + | |
| 601 | + float output = 0.0f; | |
| 602 | + | |
| 603 | + // Phasor increment | |
| 604 | + g->phase += g->inc; | |
| 605 | + while (g->phase >= 1.0f) g->phase -= 1.0f; | |
| 606 | + while (g->phase < 0.0f) g->phase += 1.0f; | |
| 607 | + | |
| 608 | + if (g->phase < g->openLength) | |
| 609 | + { | |
| 610 | + output = 0.5f*(1.0f-cosf(PI * g->phase)); | |
| 611 | + } | |
| 612 | + | |
| 613 | + else if (g->phase < g->pulseLength) | |
| 614 | + { | |
| 615 | + output = cosf(HALF_PI * (g->phase-g->openLength)/(g->pulseLength-g->openLength)); | |
| 616 | + } | |
| 617 | + | |
| 618 | + else | |
| 619 | + { | |
| 620 | + output = 0.0f; | |
| 621 | + } | |
| 622 | + return output; | |
| 623 | +} | |
| 624 | + | |
| 625 | +void tRosenbergGlottalPulse_setFreq (tRosenbergGlottalPulse* const gp, float freq) | |
| 626 | +{ | |
| 627 | + _tRosenbergGlottalPulse* g = *gp; | |
| 628 | + g->freq = freq; | |
| 629 | + g->inc = freq * leaf.invSampleRate; | |
| 630 | +} | |
| 631 | + | |
| 632 | +void tRosenbergGlottalPulse_setOpenLength (tRosenbergGlottalPulse* const gp, float openLength) | |
| 633 | +{ | |
| 634 | + _tRosenbergGlottalPulse* g = *gp; | |
| 635 | + g->openLength = openLength; | |
| 636 | +} | |
| 637 | + | |
| 638 | +void tRosenbergGlottalPulse_setPulseLength (tRosenbergGlottalPulse* const gp, float pulseLength) | |
| 639 | +{ | |
| 640 | + _tRosenbergGlottalPulse* g = *gp; | |
| 641 | + g->pulseLength = pulseLength; | |
| 642 | +} | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | +//============================================================================================================ | |
| 648 | +// SOLAD | |
| 649 | +//============================================================================================================ | |
| 650 | +/******************************************************************************/ | |
| 651 | +/***************** static function declarations *******************************/ | |
| 652 | +/******************************************************************************/ | |
| 653 | + | |
| 654 | +static void solad_init(_tSOLAD *w); | |
| 655 | +static inline float read_sample(_tSOLAD *w, float floatindex); | |
| 656 | +static void pitchdown(_tSOLAD *w, float *out); | |
| 657 | +static void pitchup(_tSOLAD *w, float *out); | |
| 658 | + | |
| 659 | +/******************************************************************************/ | |
| 660 | +/***************** public access functions ************************************/ | |
| 661 | +/******************************************************************************/ | |
| 662 | + | |
| 663 | +// init | |
| 664 | +void tSOLAD_init(tSOLAD* const wp) | |
| 665 | +{ | |
| 666 | + _tSOLAD* w = *wp = (_tSOLAD*) leaf_calloc(sizeof(_tSOLAD)); | |
| 667 | + | |
| 668 | + w->pitchfactor = 1.; | |
| 669 | + w->delaybuf = (float*) leaf_calloc(sizeof(float) * (LOOPSIZE+16)); | |
| 670 | + | |
| 671 | + solad_init(w); | |
| 672 | +} | |
| 673 | + | |
| 674 | +void tSOLAD_free(tSOLAD* const wp) | |
| 675 | +{ | |
| 676 | + _tSOLAD* w = *wp; | |
| 677 | + | |
| 678 | + leaf_free((char*)w->delaybuf); | |
| 679 | + leaf_free((char*)w); | |
| 680 | +} | |
| 681 | + | |
| 682 | +void tSOLAD_initToPool (tSOLAD* const wp, tMempool* const mp) | |
| 683 | +{ | |
| 684 | + _tMempool* m = *mp; | |
| 685 | + | |
| 686 | + _tSOLAD* w = *wp = (_tSOLAD*) mpool_calloc(sizeof(_tSOLAD), m); | |
| 687 | + | |
| 688 | + w->pitchfactor = 1.; | |
| 689 | + w->delaybuf = (float*) mpool_calloc(sizeof(float) * (LOOPSIZE+16), m); | |
| 690 | + | |
| 691 | + solad_init(w); | |
| 692 | +} | |
| 693 | + | |
| 694 | +void tSOLAD_freeFromPool (tSOLAD* const wp, tMempool* const mp) | |
| 695 | +{ | |
| 696 | + _tMempool* m = *mp; | |
| 697 | + _tSOLAD* w = *wp; | |
| 698 | + | |
| 699 | + mpool_free((char*)w->delaybuf, m); | |
| 700 | + mpool_free((char*)w, m); | |
| 701 | +} | |
| 702 | + | |
| 703 | +// send one block of input samples, receive one block of output samples | |
| 704 | +void tSOLAD_ioSamples(tSOLAD* const wp, float* in, float* out, int blocksize) | |
| 705 | +{ | |
| 706 | + _tSOLAD* w = *wp; | |
| 707 | + | |
| 708 | + int i = w->timeindex; | |
| 709 | + int n = w->blocksize = blocksize; | |
| 710 | + | |
| 711 | + if(!i) w->delaybuf[LOOPSIZE] = in[0]; // copy one sample for interpolation | |
| 712 | + while(n--) w->delaybuf[i++] = *in++; // copy one input block to delay buffer | |
| 713 | + | |
| 714 | + if(w->pitchfactor > 1) pitchup(w, out); | |
| 715 | + else pitchdown(w, out); | |
| 716 | + | |
| 717 | + w->timeindex += blocksize; | |
| 718 | + w->timeindex &= LOOPMASK; | |
| 719 | +} | |
| 720 | + | |
| 721 | +// set periodicity analysis data | |
| 722 | +void tSOLAD_setPeriod(tSOLAD* const wp, float period) | |
| 723 | +{ | |
| 724 | + _tSOLAD* w = *wp; | |
| 725 | + | |
| 726 | + if(period > MAXPERIOD) period = MAXPERIOD; | |
| 727 | + if(period > MINPERIOD) w->period = period; // ignore period when too small | |
| 728 | +} | |
| 729 | + | |
| 730 | +// set pitch factor between 0.25 and 4 | |
| 731 | +void tSOLAD_setPitchFactor(tSOLAD* const wp, float pitchfactor) | |
| 732 | +{ | |
| 733 | + _tSOLAD* w = *wp; | |
| 734 | + | |
| 735 | + if (pitchfactor <= 0.0f) return; | |
| 736 | + w->pitchfactor = pitchfactor; | |
| 737 | +} | |
| 738 | + | |
| 739 | +// force readpointer lag | |
| 740 | +void tSOLAD_setReadLag(tSOLAD* const wp, float readlag) | |
| 741 | +{ | |
| 742 | + _tSOLAD* w = *wp; | |
| 743 | + | |
| 744 | + if(readlag < 0) readlag = 0; | |
| 745 | + if(readlag < w->readlag) // do not jump backward, only forward | |
| 746 | + { | |
| 747 | + w->jump = w->readlag - readlag; | |
| 748 | + w->readlag = readlag; | |
| 749 | + w->xfadelength = readlag; | |
| 750 | + w->xfadevalue = 1; | |
| 751 | + } | |
| 752 | +} | |
| 753 | + | |
| 754 | +// reset state variables | |
| 755 | +void tSOLAD_resetState(tSOLAD* const wp) | |
| 756 | +{ | |
| 757 | + _tSOLAD* w = *wp; | |
| 758 | + | |
| 759 | + int n = LOOPSIZE + 1; | |
| 760 | + float *buf = w->delaybuf; | |
| 761 | + | |
| 762 | + while(n--) *buf++ = 0; | |
| 763 | + solad_init(w); | |
| 764 | +} | |
| 765 | + | |
| 766 | +/******************************************************************************/ | |
| 767 | +/******************** private procedures **************************************/ | |
| 768 | +/******************************************************************************/ | |
| 769 | + | |
| 770 | +/* | |
| 771 | + Function pitchdown() is called to read samples from the delay buffer when pitch | |
| 772 | + factor is between 0.25 and 1. The read pointer lags behind because of the slowed | |
| 773 | + down speed, and it must jump forward towards the write pointer soon as there is | |
| 774 | + sufficient space to jump. That is, if there is at least one period of the input | |
| 775 | + signal between read pointer and write pointer. When short periods follow up on | |
| 776 | + long periods, the read pointer may have space to jump over more than one period | |
| 777 | + lenghts. Jump length must be [periodlength ^ 2] in any case. | |
| 778 | + | |
| 779 | + A linear crossfade function joins the jump-from point with the jump-to point. | |
| 780 | + The crossfade must be completed before another read pointer jump is allowed. | |
| 781 | + Length of the crossfade function is stored as a number of samples in terms of | |
| 782 | + the input sample rate. This length is dynamically translated | |
| 783 | + to a crossfade length expressed in output reading rate, according to pitch | |
| 784 | + factor which can change before the crossfade is completed. Crossfade length does | |
| 785 | + not cover an invariable length in periods for all pitch transposition factors. | |
| 786 | + For pitch factors from 0.5 till 1, crossfade length is stretched in the | |
| 787 | + output just as much as the signal itself, as crossfade speed is set to equal | |
| 788 | + pitch factor. For pitch factors below 0.5, the read pointer wants to jump | |
| 789 | + forward before one period is read, therefore the crossfade length as expressed | |
| 790 | + in output periods must be shorter. Crossfade speed is set to [1 - pitchfactor] | |
| 791 | + for those cases. Pitch factor 0.5 is the natural switch point between crossfade | |
| 792 | + speeds [pitchfactor] and [1 - pitchfactor] because 0.5 == 1 - 0.5. The crossfade | |
| 793 | + speed modification for pitch factors below 0.5 also means that much of the | |
| 794 | + original signal content will be skipped. | |
| 795 | + */ | |
| 796 | + | |
| 797 | + | |
| 798 | +static void pitchdown(_tSOLAD* const w, float *out) | |
| 799 | +{ | |
| 800 | + int n = w->blocksize; | |
| 801 | + float refindex = (float)(w->timeindex + LOOPSIZE); // no negative values! | |
| 802 | + float pitchfactor = w->pitchfactor; | |
| 803 | + float period = w->period; | |
| 804 | + float readlag = w->readlag; | |
| 805 | + float readlagstep = 1 - pitchfactor; | |
| 806 | + float jump = w->jump; | |
| 807 | + float xfadevalue = w->xfadevalue; | |
| 808 | + float xfadelength = w->xfadelength; | |
| 809 | + float xfadespeed, xfadestep, readindex, outputsample; | |
| 810 | + | |
| 811 | + if(pitchfactor > 0.5) xfadespeed = pitchfactor; | |
| 812 | + else xfadespeed = 1 - pitchfactor; | |
| 813 | + xfadestep = xfadespeed / xfadelength; | |
| 814 | + | |
| 815 | + while(n--) | |
| 816 | + { | |
| 817 | + if(readlag > period) // check if read pointer may jump forward... | |
| 818 | + { | |
| 819 | + if(xfadevalue <= 0) // ...but do not interrupt crossfade | |
| 820 | + { | |
| 821 | + jump = period; // jump forward | |
| 822 | + while((jump * 2) < readlag) jump *= 2; // use available space | |
| 823 | + readlag -= jump; // reduce read pointer lag | |
| 824 | + xfadevalue = 1; // start crossfade | |
| 825 | + xfadelength = period - 1; | |
| 826 | + xfadestep = xfadespeed / xfadelength; | |
| 827 | + } | |
| 828 | + } | |
| 829 | + | |
| 830 | + readindex = refindex - readlag; | |
| 831 | + outputsample = read_sample(w, readindex); | |
| 832 | + | |
| 833 | + if(xfadevalue > 0) | |
| 834 | + { | |
| 835 | + outputsample *= (1 - xfadevalue); // fadein | |
| 836 | + outputsample += read_sample(w, readindex - jump) * xfadevalue; // fadeout | |
| 837 | + xfadevalue -= xfadestep; | |
| 838 | + } | |
| 839 | + | |
| 840 | + *out++ = outputsample; | |
| 841 | + refindex += 1; | |
| 842 | + readlag += readlagstep; | |
| 843 | + } | |
| 844 | + | |
| 845 | + w->jump = jump; // state variables | |
| 846 | + w->readlag = readlag; | |
| 847 | + w->xfadevalue = xfadevalue; | |
| 848 | + w->xfadelength = xfadelength; | |
| 849 | +} | |
| 850 | + | |
| 851 | + | |
| 852 | +/* | |
| 853 | + Function pitchup() for pitch factors above 1 is more complicated than | |
| 854 | + pitchdown(). The read pointer increments faster than the write pointer and a | |
| 855 | + backward jump must happen in time, reckoning with the crossfade region. The read | |
| 856 | + pointer backward jump length is always one period. In order to minimize the area | |
| 857 | + of signal duplicates, crossfade length is aimed at [period / pitchfactor]. | |
| 858 | + This leads to a crossfade speed of [pitchfactor * pitchfactor]. | |
| 859 | + | |
| 860 | + Some samples for the fade out (but not all of them) must already be in the | |
| 861 | + buffer, otherwise we will run out of input samples before the crossfade is | |
| 862 | + completed. The ratio of past samples and future samples for a crossfade of any | |
| 863 | + length is as follows: | |
| 864 | + | |
| 865 | + past samples: xfadelength * (1 - 1 / pitchfactor) | |
| 866 | + future samples: xfadelength * (1 / pitchfactor) | |
| 867 | + | |
| 868 | + For example in the case of pitch factor 1.5 this would be: | |
| 869 | + | |
| 870 | + past samples: xfadelength * (1 - 1 / 1.5) = xfadelength * 1 / 3 | |
| 871 | + future samples: xfadelength * (1 / 1.5) = xfadelength * 2 / 3 | |
| 872 | + | |
| 873 | + In the case of pitch factor 4 this would be: | |
| 874 | + | |
| 875 | + past samples: xfadelength * (1 - 1 / 4) = xfadelength * 3 / 4 | |
| 876 | + future samples: xfadelength * (1 / 4) = xfadelength * 1 / 4 | |
| 877 | + | |
| 878 | + The read pointer lag must therefore preserve a minimum dependent on pitch | |
| 879 | + factor. The minimum is called 'limit' here: | |
| 880 | + | |
| 881 | + limit = period * (pitchfactor - 1) / pitchfactor * pitchfactor | |
| 882 | + | |
| 883 | + Components of this expression are combined to reuse them in operations, while | |
| 884 | + (pitchfactor - 1) is changed to (pitchfactor - 0.99) to avoid numerical | |
| 885 | + resolution issues for pitch factors slightly above 1: | |
| 886 | + | |
| 887 | + xfadespeed = pitchfactor * pitchfactor | |
| 888 | + limitfactor = (pitchfactor - 0.99) / xfadespeed | |
| 889 | + limit = period * limitfactor | |
| 890 | + | |
| 891 | + When read lag is smaller than this limit, the read pointer must preferably | |
| 892 | + jump backward, unless a previous crossfade is not yet completed. Crossfades must | |
| 893 | + preferably be completed, unless the read pointer lag becomes smaller than zero. | |
| 894 | + With fluctuating period lengths and pitch factors, the readpointer lag limit may | |
| 895 | + change from one input block to the next in such a way that the actual lag is | |
| 896 | + suddenly much smaller than the limit, and the intended crossfade length can not | |
| 897 | + be applied. Therefore the crossfade length is simply calculated from the | |
| 898 | + available amount of samples for all cases, like so: | |
| 899 | + | |
| 900 | + xfadelength = readlag / limitfactor | |
| 901 | + | |
| 902 | + For most occurrences, this will amount to a crossfade length reduced to | |
| 903 | + [period / pitchfactor] in the output for pitch factors above 1, while in some | |
| 904 | + cases it will be considerably shorter. Fortunately, an incidental aberration of | |
| 905 | + the intended crossfade length hardly ever creates an audible artifact. The | |
| 906 | + reason to specify preferred crossfade length according to pitch factor is to | |
| 907 | + minimize the impression of echoes without sacrificing too much of the signal | |
| 908 | + content. The readpointer jump length remains one period in any case. | |
| 909 | + | |
| 910 | + Sometimes, the input signal periodicity may decrease substantially between one | |
| 911 | + signal block and the next. In such cases it may be possible for the read pointer | |
| 912 | + to jump forward and reduce latency. For every signal block, a check on this | |
| 913 | + possibility is done. A previous crossfade must be completed before a forward | |
| 914 | + jump is allowed. | |
| 915 | + */ | |
| 916 | +static void pitchup(_tSOLAD* const w, float *out) | |
| 917 | +{ | |
| 918 | + int n = w->blocksize; | |
| 919 | + float refindex = (float)(w->timeindex + LOOPSIZE); // no negative values | |
| 920 | + float pitchfactor = w->pitchfactor; | |
| 921 | + float period = w->period; | |
| 922 | + float readlag = w->readlag; | |
| 923 | + float jump = w->jump; | |
| 924 | + float xfadevalue = w->xfadevalue; | |
| 925 | + float xfadelength = w->xfadelength; | |
| 926 | + | |
| 927 | + float readlagstep = pitchfactor - 1; | |
| 928 | + float xfadespeed = pitchfactor * pitchfactor; | |
| 929 | + float xfadestep = xfadespeed / xfadelength; | |
| 930 | + float limitfactor = (pitchfactor - (float)0.99) / xfadespeed; | |
| 931 | + float limit = period * limitfactor; | |
| 932 | + float readindex, outputsample; | |
| 933 | + | |
| 934 | + if((readlag > (period + 2 * limit)) & (xfadevalue < 0)) | |
| 935 | + { | |
| 936 | + jump = period; // jump forward | |
| 937 | + while((jump * 2) < (readlag - 2 * limit)) jump *= 2; // use available space | |
| 938 | + readlag -= jump; // reduce read pointer lag | |
| 939 | + xfadevalue = 1; // start crossfade | |
| 940 | + xfadelength = period - 1; | |
| 941 | + xfadestep = xfadespeed / xfadelength; | |
| 942 | + } | |
| 943 | + | |
| 944 | + while(n--) | |
| 945 | + { | |
| 946 | + if(readlag < limit) // check if read pointer should jump backward... | |
| 947 | + { | |
| 948 | + if((xfadevalue < 0) | (readlag < 0)) // ...but try not to interrupt crossfade | |
| 949 | + { | |
| 950 | + xfadelength = readlag / limitfactor; | |
| 951 | + if(xfadelength < 1) xfadelength = 1; | |
| 952 | + xfadestep = xfadespeed / xfadelength; | |
| 953 | + | |
| 954 | + jump = -period; // jump backward | |
| 955 | + readlag += period; // increase read pointer lag | |
| 956 | + xfadevalue = 1; // start crossfade | |
| 957 | + } | |
| 958 | + } | |
| 959 | + | |
| 960 | + readindex = refindex - readlag; | |
| 961 | + outputsample = read_sample(w, readindex); | |
| 962 | + | |
| 963 | + if(xfadevalue > 0) | |
| 964 | + { | |
| 965 | + outputsample *= (1 - xfadevalue); | |
| 966 | + outputsample += read_sample(w, readindex - jump) * xfadevalue; | |
| 967 | + xfadevalue -= xfadestep; | |
| 968 | + } | |
| 969 | + | |
| 970 | + *out++ = outputsample; | |
| 971 | + refindex += 1; | |
| 972 | + readlag -= readlagstep; | |
| 973 | + } | |
| 974 | + | |
| 975 | + w->readlag = readlag; // state variables | |
| 976 | + w->jump = jump; | |
| 977 | + w->xfadelength = xfadelength; | |
| 978 | + w->xfadevalue = xfadevalue; | |
| 979 | +} | |
| 980 | + | |
| 981 | +// read one sample from delay buffer, with linear interpolation | |
| 982 | +static inline float read_sample(_tSOLAD* const w, float floatindex) | |
| 983 | +{ | |
| 984 | + int index = (int)floatindex; | |
| 985 | + float fraction = floatindex - (float)index; | |
| 986 | + float *buf = w->delaybuf; | |
| 987 | + index &= LOOPMASK; | |
| 988 | + | |
| 989 | + return (buf[index] + (fraction * (buf[index+1] - buf[index]))); | |
| 990 | +} | |
| 991 | + | |
| 992 | +static void solad_init(_tSOLAD* const w) | |
| 993 | +{ | |
| 994 | + w->timeindex = 0; | |
| 995 | + w->xfadevalue = -1; | |
| 996 | + w->period = INITPERIOD; | |
| 997 | + w->readlag = INITPERIOD; | |
| 998 | + w->blocksize = INITPERIOD; | |
| 999 | +} | |
| 1000 | + | |
| 1001 | +//============================================================================================================ | |
| 1002 | +// PITCHSHIFT | |
| 1003 | +//============================================================================================================ | |
| 1004 | + | |
| 1005 | +static int pitchshift_attackdetect(_tPitchShift* ps) | |
| 1006 | +{ | |
| 1007 | + float envout; | |
| 1008 | + | |
| 1009 | + _tPeriodDetection* p = *ps->p; | |
| 1010 | + | |
| 1011 | + envout = tEnvPD_tick(&p->env); | |
| 1012 | + | |
| 1013 | + if (envout >= 1.0f) | |
| 1014 | + { | |
| 1015 | + p->lastmax = p->max; | |
| 1016 | + if (envout > p->max) | |
| 1017 | + { | |
| 1018 | + p->max = envout; | |
| 1019 | + } | |
| 1020 | + else | |
| 1021 | + { | |
| 1022 | + p->deltamax = envout - p->max; | |
| 1023 | + p->max = p->max * ps->radius; | |
| 1024 | + } | |
| 1025 | + p->deltamax = p->max - p->lastmax; | |
| 1026 | + } | |
| 1027 | + | |
| 1028 | + p->fba = p->fba ? (p->fba - 1) : 0; | |
| 1029 | + | |
| 1030 | + return (p->fba == 0 && (p->max > 60 && p->deltamax > 6)) ? 1 : 0; | |
| 1031 | +} | |
| 1032 | + | |
| 1033 | +void tPitchShift_init (tPitchShift* const psr, tPeriodDetection* pd, float* out, int bufSize) | |
| 1034 | +{ | |
| 1035 | + _tPitchShift* ps = *psr = (_tPitchShift*) leaf_calloc(sizeof(_tPitchShift)); | |
| 1036 | + _tPeriodDetection* p = *pd; | |
| 1037 | + | |
| 1038 | + ps->p = pd; | |
| 1039 | + | |
| 1040 | + ps->outBuffer = out; | |
| 1041 | + ps->bufSize = bufSize; | |
| 1042 | + ps->frameSize = p->frameSize; | |
| 1043 | + ps->framesPerBuffer = ps->bufSize / ps->frameSize; | |
| 1044 | + ps->curBlock = 1; | |
| 1045 | + ps->lastBlock = 0; | |
| 1046 | + ps->index = 0; | |
| 1047 | + ps->pitchFactor = 1.0f; | |
| 1048 | + | |
| 1049 | + tSOLAD_init(&ps->sola); | |
| 1050 | + | |
| 1051 | + tHighpass_init(&ps->hp, HPFREQ); | |
| 1052 | + | |
| 1053 | + tSOLAD_setPitchFactor(&ps->sola, DEFPITCHRATIO); | |
| 1054 | +} | |
| 1055 | + | |
| 1056 | +void tPitchShift_free(tPitchShift* const psr) | |
| 1057 | +{ | |
| 1058 | + _tPitchShift* ps = *psr; | |
| 1059 | + | |
| 1060 | + tSOLAD_free(&ps->sola); | |
| 1061 | + tHighpass_free(&ps->hp); | |
| 1062 | + leaf_free((char*)ps); | |
| 1063 | +} | |
| 1064 | + | |
| 1065 | +void tPitchShift_initToPool (tPitchShift* const psr, tPeriodDetection* const pd, float* out, int bufSize, tMempool* const mp) | |
| 1066 | +{ | |
| 1067 | + _tMempool* m = *mp; | |
| 1068 | + | |
| 1069 | + _tPitchShift* ps = *psr = (_tPitchShift*) mpool_calloc(sizeof(_tPitchShift), m); | |
| 1070 | + | |
| 1071 | + _tPeriodDetection* p = *pd; | |
| 1072 | + | |
| 1073 | + ps->p = pd; | |
| 1074 | + | |
| 1075 | + ps->outBuffer = out; | |
| 1076 | + ps->bufSize = bufSize; | |
| 1077 | + ps->frameSize = p->frameSize; | |
| 1078 | + ps->framesPerBuffer = ps->bufSize / ps->frameSize; | |
| 1079 | + ps->curBlock = 1; | |
| 1080 | + ps->lastBlock = 0; | |
| 1081 | + ps->index = 0; | |
| 1082 | + ps->pitchFactor = 1.0f; | |
| 1083 | + | |
| 1084 | + tSOLAD_initToPool(&ps->sola, mp); | |
| 1085 | + | |
| 1086 | + tHighpass_initToPool(&ps->hp, HPFREQ, mp); | |
| 1087 | + | |
| 1088 | + tSOLAD_setPitchFactor(&ps->sola, DEFPITCHRATIO); | |
| 1089 | +} | |
| 1090 | + | |
| 1091 | +void tPitchShift_freeFromPool (tPitchShift* const psr, tMempool* const mp) | |
| 1092 | +{ | |
| 1093 | + _tMempool* m = *mp; | |
| 1094 | + _tPitchShift* ps = *psr; | |
| 1095 | + | |
| 1096 | + tSOLAD_freeFromPool(&ps->sola, mp); | |
| 1097 | + tHighpass_freeFromPool(&ps->hp, mp); | |
| 1098 | + mpool_free((char*)ps, m); | |
| 1099 | +} | |
| 1100 | + | |
| 1101 | +void tPitchShift_setPitchFactor(tPitchShift* psr, float pf) | |
| 1102 | +{ | |
| 1103 | + _tPitchShift* ps = *psr; | |
| 1104 | + | |
| 1105 | + ps->pitchFactor = pf; | |
| 1106 | +} | |
| 1107 | + | |
| 1108 | +float tPitchShift_shift (tPitchShift* psr) | |
| 1109 | +{ | |
| 1110 | + _tPitchShift* ps = *psr; | |
| 1111 | + _tPeriodDetection* p = *ps->p; | |
| 1112 | + | |
| 1113 | + float period, out; | |
| 1114 | + int i, iLast; | |
| 1115 | + | |
| 1116 | + i = p->i; | |
| 1117 | + iLast = p->iLast; | |
| 1118 | + | |
| 1119 | + out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); | |
| 1120 | + | |
| 1121 | + if (p->indexstore >= ps->frameSize) | |
| 1122 | + { | |
| 1123 | + period = tPeriodDetection_getPeriod(&p); | |
| 1124 | + | |
| 1125 | + if(pitchshift_attackdetect(ps) == 1) | |
| 1126 | + { | |
| 1127 | + p->fba = 5; | |
| 1128 | + tSOLAD_setReadLag(&ps->sola, p->windowSize); | |
| 1129 | + } | |
| 1130 | + | |
| 1131 | + tSOLAD_setPeriod(&ps->sola, period); | |
| 1132 | + tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); | |
| 1133 | + | |
| 1134 | + tSOLAD_ioSamples(&ps->sola, &(p->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); | |
| 1135 | + } | |
| 1136 | + | |
| 1137 | + return out; | |
| 1138 | +} | |
| 1139 | + | |
| 1140 | +float tPitchShift_shiftToFreq (tPitchShift* psr, float freq) | |
| 1141 | +{ | |
| 1142 | + _tPitchShift* ps = *psr; | |
| 1143 | + _tPeriodDetection* p = *ps->p; | |
| 1144 | + | |
| 1145 | + float period, out; | |
| 1146 | + int i, iLast; | |
| 1147 | + | |
| 1148 | + i = p->i; | |
| 1149 | + iLast = p->iLast; | |
| 1150 | + | |
| 1151 | + out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); | |
| 1152 | + | |
| 1153 | + if (p->indexstore >= ps->frameSize) | |
| 1154 | + { | |
| 1155 | + period = tPeriodDetection_getPeriod(&p); | |
| 1156 | + | |
| 1157 | + if(pitchshift_attackdetect(ps) == 1) | |
| 1158 | + { | |
| 1159 | + p->fba = 5; | |
| 1160 | + tSOLAD_setReadLag(&ps->sola, p->windowSize); | |
| 1161 | + } | |
| 1162 | + | |
| 1163 | + tSOLAD_setPeriod(&ps->sola, period); | |
| 1164 | + | |
| 1165 | + if (period != 0) ps->pitchFactor = period*freq*leaf.invSampleRate; | |
| 1166 | + else ps->pitchFactor = 1.0f; | |
| 1167 | + | |
| 1168 | + tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); | |
| 1169 | + | |
| 1170 | + tSOLAD_ioSamples(&ps->sola, &(p->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); | |
| 1171 | + } | |
| 1172 | + return out; | |
| 1173 | +} | |
| 1174 | + | |
| 1175 | +float tPitchShift_shiftToFunc (tPitchShift* psr, float (*fun)(float)) | |
| 1176 | +{ | |
| 1177 | + _tPitchShift* ps = *psr; | |
| 1178 | + _tPeriodDetection* p = *ps->p; | |
| 1179 | + | |
| 1180 | + float period, out; | |
| 1181 | + int i, iLast; | |
| 1182 | + | |
| 1183 | + i = p->i; | |
| 1184 | + iLast = p->iLast; | |
| 1185 | + | |
| 1186 | + out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); | |
| 1187 | + | |
| 1188 | + if (p->indexstore >= ps->frameSize) | |
| 1189 | + { | |
| 1190 | + period = tPeriodDetection_getPeriod(&p); | |
| 1191 | + | |
| 1192 | + if(pitchshift_attackdetect(ps) == 1) | |
| 1193 | + { | |
| 1194 | + p->fba = 5; | |
| 1195 | + tSOLAD_setReadLag(&ps->sola, p->windowSize); | |
| 1196 | + } | |
| 1197 | + | |
| 1198 | + tSOLAD_setPeriod(&ps->sola, period); | |
| 1199 | + | |
| 1200 | + ps->pitchFactor = period/fun(period); | |
| 1201 | + tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); | |
| 1202 | + | |
| 1203 | + tSOLAD_ioSamples(&ps->sola, &(p->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); | |
| 1204 | + | |
| 1205 | + ps->curBlock++; | |
| 1206 | + if (ps->curBlock >= p->framesPerBuffer) ps->curBlock = 0; | |
| 1207 | + ps->lastBlock++; | |
| 1208 | + if (ps->lastBlock >= ps->framesPerBuffer) ps->lastBlock = 0; | |
| 1209 | + } | |
| 1210 | + | |
| 1211 | + return out; | |
| 1212 | +} | |
| 1213 | + | |
| 1214 | +//============================================================================================================ | |
| 1215 | +// RETUNE | |
| 1216 | +//============================================================================================================ | |
| 1217 | + | |
| 1218 | +void tRetune_init(tRetune* const rt, int numVoices, int bufSize, int frameSize) | |
| 1219 | +{ | |
| 1220 | + tRetune_initToPool(rt, numVoices, bufSize, frameSize, &leaf.mempool); | |
| 1221 | +} | |
| 1222 | + | |
| 1223 | +void tRetune_free(tRetune* const rt) | |
| 1224 | +{ | |
| 1225 | + tRetune_freeFromPool(rt, &leaf.mempool); | |
| 1226 | +} | |
| 1227 | + | |
| 1228 | +void tRetune_initToPool (tRetune* const rt, int numVoices, int bufSize, int frameSize, tMempool* const mp) | |
| 1229 | +{ | |
| 1230 | + _tMempool* m = *mp; | |
| 1231 | + _tRetune* r = *rt = (_tRetune*) mpool_alloc(sizeof(_tRetune), m); | |
| 1232 | + | |
| 1233 | + r->bufSize = bufSize; | |
| 1234 | + r->frameSize = frameSize; | |
| 1235 | + r->numVoices = numVoices; | |
| 1236 | + | |
| 1237 | + r->inBuffer = (float*) mpool_calloc(sizeof(float) * r->bufSize, m); | |
| 1238 | + r->outBuffers = (float**) mpool_calloc(sizeof(float*) * r->numVoices, m); | |
| 1239 | + | |
| 1240 | + r->hopSize = DEFHOPSIZE; | |
| 1241 | + r->windowSize = DEFWINDOWSIZE; | |
| 1242 | + r->fba = FBA; | |
| 1243 | + tRetune_setTimeConstant(rt, DEFTIMECONSTANT); | |
| 1244 | + | |
| 1245 | + r->inputPeriod = 0.0f; | |
| 1246 | + | |
| 1247 | + r->ps = (tPitchShift*) mpool_calloc(sizeof(tPitchShift) * r->numVoices, m); | |
| 1248 | + r->pitchFactor = (float*) mpool_calloc(sizeof(float) * r->numVoices, m); | |
| 1249 | + r->tickOutput = (float*) mpool_calloc(sizeof(float) * r->numVoices, m); | |
| 1250 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1251 | + { | |
| 1252 | + r->outBuffers[i] = (float*) mpool_calloc(sizeof(float) * r->bufSize, m); | |
| 1253 | + } | |
| 1254 | + | |
| 1255 | + tPeriodDetection_initToPool(&r->pd, r->inBuffer, r->outBuffers[0], r->bufSize, r->frameSize, mp); | |
| 1256 | + | |
| 1257 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1258 | + { | |
| 1259 | + tPitchShift_initToPool(&r->ps[i], &r->pd, r->outBuffers[i], r->bufSize, mp); | |
| 1260 | + } | |
| 1261 | +} | |
| 1262 | + | |
| 1263 | +void tRetune_freeFromPool (tRetune* const rt, tMempool* const mp) | |
| 1264 | +{ | |
| 1265 | + _tMempool* m = *mp; | |
| 1266 | + _tRetune* r = *rt; | |
| 1267 | + | |
| 1268 | + tPeriodDetection_freeFromPool(&r->pd, mp); | |
| 1269 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1270 | + { | |
| 1271 | + tPitchShift_freeFromPool(&r->ps[i], mp); | |
| 1272 | + mpool_free((char*)r->outBuffers[i], m); | |
| 1273 | + } | |
| 1274 | + mpool_free((char*)r->tickOutput, m); | |
| 1275 | + mpool_free((char*)r->pitchFactor, m); | |
| 1276 | + mpool_free((char*)r->ps, m); | |
| 1277 | + mpool_free((char*)r->inBuffer, m); | |
| 1278 | + mpool_free((char*)r->outBuffers, m); | |
| 1279 | + mpool_free((char*)r, m); | |
| 1280 | +} | |
| 1281 | + | |
| 1282 | +float* tRetune_tick(tRetune* const rt, float sample) | |
| 1283 | +{ | |
| 1284 | + _tRetune* r = *rt; | |
| 1285 | + | |
| 1286 | + r->inputPeriod = tPeriodDetection_tick(&r->pd, sample); | |
| 1287 | + | |
| 1288 | + for (int v = 0; v < r->numVoices; ++v) | |
| 1289 | + { | |
| 1290 | + r->tickOutput[v] = tPitchShift_shift(&r->ps[v]); | |
| 1291 | + } | |
| 1292 | + | |
| 1293 | + return r->tickOutput; | |
| 1294 | +} | |
| 1295 | + | |
| 1296 | +void tRetune_setNumVoices(tRetune* const rt, int numVoices) | |
| 1297 | +{ | |
| 1298 | + _tRetune* r = *rt; | |
| 1299 | + | |
| 1300 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1301 | + { | |
| 1302 | + tPitchShift_free(&r->ps[i]); | |
| 1303 | + leaf_free((char*)r->outBuffers[i]); | |
| 1304 | + } | |
| 1305 | + leaf_free((char*)r->tickOutput); | |
| 1306 | + leaf_free((char*)r->pitchFactor); | |
| 1307 | + leaf_free((char*)r->ps); | |
| 1308 | + leaf_free((char*)r->outBuffers); | |
| 1309 | + | |
| 1310 | + r->numVoices = numVoices; | |
| 1311 | + | |
| 1312 | + r->outBuffers = (float**) leaf_alloc(sizeof(float*) * r->numVoices); | |
| 1313 | + r->ps = (tPitchShift*) leaf_alloc(sizeof(tPitchShift) * r->numVoices); | |
| 1314 | + r->pitchFactor = (float*) leaf_alloc(sizeof(float) * r->numVoices); | |
| 1315 | + r->tickOutput = (float*) leaf_alloc(sizeof(float) * r->numVoices); | |
| 1316 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1317 | + { | |
| 1318 | + r->outBuffers[i] = (float*) leaf_alloc(sizeof(float) * r->bufSize); | |
| 1319 | + tPitchShift_init(&r->ps[i], &r->pd, r->outBuffers[i], r->bufSize); | |
| 1320 | + } | |
| 1321 | +} | |
| 1322 | + | |
| 1323 | +void tRetune_setPitchFactors(tRetune* const rt, float pf) | |
| 1324 | +{ | |
| 1325 | + _tRetune* r = *rt; | |
| 1326 | + | |
| 1327 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1328 | + { | |
| 1329 | + r->pitchFactor[i] = pf; | |
| 1330 | + tPitchShift_setPitchFactor(&r->ps[i], r->pitchFactor[i]); | |
| 1331 | + } | |
| 1332 | +} | |
| 1333 | + | |
| 1334 | +void tRetune_setPitchFactor(tRetune* const rt, float pf, int voice) | |
| 1335 | +{ | |
| 1336 | + _tRetune* r = *rt; | |
| 1337 | + | |
| 1338 | + r->pitchFactor[voice] = pf; | |
| 1339 | + tPitchShift_setPitchFactor(&r->ps[voice], r->pitchFactor[voice]); | |
| 1340 | +} | |
| 1341 | + | |
| 1342 | +void tRetune_setTimeConstant(tRetune* const rt, float tc) | |
| 1343 | +{ | |
| 1344 | + _tRetune* r = *rt; | |
| 1345 | + | |
| 1346 | + r->timeConstant = tc; | |
| 1347 | + r->radius = expf(-1000.0f * r->hopSize * leaf.invSampleRate / r->timeConstant); | |
| 1348 | +} | |
| 1349 | + | |
| 1350 | +void tRetune_setHopSize(tRetune* const rt, int hs) | |
| 1351 | +{ | |
| 1352 | + _tRetune* r = *rt; | |
| 1353 | + | |
| 1354 | + r->hopSize = hs; | |
| 1355 | + tPeriodDetection_setHopSize(&r->pd, r->hopSize); | |
| 1356 | +} | |
| 1357 | + | |
| 1358 | +void tRetune_setWindowSize(tRetune* const rt, int ws) | |
| 1359 | +{ | |
| 1360 | + _tRetune* r = *rt; | |
| 1361 | + | |
| 1362 | + r->windowSize = ws; | |
| 1363 | + tPeriodDetection_setWindowSize(&r->pd, r->windowSize); | |
| 1364 | +} | |
| 1365 | + | |
| 1366 | +void tRetune_setFidelityThreshold(tRetune* const rt, float threshold) | |
| 1367 | +{ | |
| 1368 | + _tRetune* r = *rt; | |
| 1369 | + | |
| 1370 | + tPeriodDetection_setFidelityThreshold(&r->pd, threshold); | |
| 1371 | +} | |
| 1372 | + | |
| 1373 | +float tRetune_getInputPeriod(tRetune* const rt) | |
| 1374 | +{ | |
| 1375 | + _tRetune* r = *rt; | |
| 1376 | + | |
| 1377 | + return (r->inputPeriod * leaf.invSampleRate); | |
| 1378 | +} | |
| 1379 | + | |
| 1380 | +float tRetune_getInputFreq(tRetune* const rt) | |
| 1381 | +{ | |
| 1382 | + _tRetune* r = *rt; | |
| 1383 | + | |
| 1384 | + return 1.0f/(r->inputPeriod * leaf.invSampleRate); | |
| 1385 | +} | |
| 1386 | + | |
| 1387 | +//============================================================================================================ | |
| 1388 | +// AUTOTUNE | |
| 1389 | +//============================================================================================================ | |
| 1390 | + | |
| 1391 | +void tAutotune_init(tAutotune* const rt, int numVoices, int bufSize, int frameSize) | |
| 1392 | +{ | |
| 1393 | + tAutotune_initToPool(rt, numVoices, bufSize, frameSize, &leaf.mempool); | |
| 1394 | +} | |
| 1395 | + | |
| 1396 | +void tAutotune_free(tAutotune* const rt) | |
| 1397 | +{ | |
| 1398 | + tAutotune_freeFromPool(rt, &leaf.mempool); | |
| 1399 | +} | |
| 1400 | + | |
| 1401 | +void tAutotune_initToPool (tAutotune* const rt, int numVoices, int bufSize, int frameSize, tMempool* const mp) | |
| 1402 | +{ | |
| 1403 | + _tMempool* m = *mp; | |
| 1404 | + _tAutotune* r = *rt = (_tAutotune*) mpool_alloc(sizeof(_tAutotune), m); | |
| 1405 | + | |
| 1406 | + r->bufSize = bufSize; | |
| 1407 | + r->frameSize = frameSize; | |
| 1408 | + r->numVoices = numVoices; | |
| 1409 | + | |
| 1410 | + r->inBuffer = (float*) mpool_alloc(sizeof(float) * r->bufSize, m); | |
| 1411 | + r->outBuffers = (float**) mpool_alloc(sizeof(float*) * r->numVoices, m); | |
| 1412 | + | |
| 1413 | + r->hopSize = DEFHOPSIZE; | |
| 1414 | + r->windowSize = DEFWINDOWSIZE; | |
| 1415 | + r->fba = FBA; | |
| 1416 | + tAutotune_setTimeConstant(rt, DEFTIMECONSTANT); | |
| 1417 | + | |
| 1418 | + r->ps = (tPitchShift*) mpool_alloc(sizeof(tPitchShift) * r->numVoices, m); | |
| 1419 | + r->freq = (float*) mpool_alloc(sizeof(float) * r->numVoices, m); | |
| 1420 | + r->tickOutput = (float*) mpool_alloc(sizeof(float) * r->numVoices, m); | |
| 1421 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1422 | + { | |
| 1423 | + r->outBuffers[i] = (float*) mpool_alloc(sizeof(float) * r->bufSize, m); | |
| 1424 | + } | |
| 1425 | + | |
| 1426 | + tPeriodDetection_initToPool(&r->pd, r->inBuffer, r->outBuffers[0], r->bufSize, r->frameSize, mp); | |
| 1427 | + | |
| 1428 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1429 | + { | |
| 1430 | + tPitchShift_initToPool(&r->ps[i], &r->pd, r->outBuffers[i], r->bufSize, mp); | |
| 1431 | + } | |
| 1432 | + | |
| 1433 | + r->inputPeriod = 0.0f; | |
| 1434 | +} | |
| 1435 | + | |
| 1436 | +void tAutotune_freeFromPool (tAutotune* const rt, tMempool* const mp) | |
| 1437 | +{ | |
| 1438 | + _tMempool* m = *mp; | |
| 1439 | + _tAutotune* r = *rt; | |
| 1440 | + | |
| 1441 | + tPeriodDetection_freeFromPool(&r->pd, mp); | |
| 1442 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1443 | + { | |
| 1444 | + tPitchShift_freeFromPool(&r->ps[i], mp); | |
| 1445 | + mpool_free((char*)r->outBuffers[i], m); | |
| 1446 | + } | |
| 1447 | + mpool_free((char*)r->tickOutput, m); | |
| 1448 | + mpool_free((char*)r->freq, m); | |
| 1449 | + mpool_free((char*)r->ps, m); | |
| 1450 | + mpool_free((char*)r->inBuffer, m); | |
| 1451 | + mpool_free((char*)r->outBuffers, m); | |
| 1452 | + mpool_free((char*)r, m); | |
| 1453 | +} | |
| 1454 | + | |
| 1455 | +float* tAutotune_tick(tAutotune* const rt, float sample) | |
| 1456 | +{ | |
| 1457 | + _tAutotune* r = *rt; | |
| 1458 | + | |
| 1459 | + float tempPeriod = tPeriodDetection_tick(&r->pd, sample); | |
| 1460 | + if (tempPeriod < 1000.0f) //to avoid trying to follow consonants JS | |
| 1461 | + { | |
| 1462 | + r->inputPeriod = tempPeriod; | |
| 1463 | + } | |
| 1464 | + | |
| 1465 | + for (int v = 0; v < r->numVoices; ++v) | |
| 1466 | + { | |
| 1467 | + r->tickOutput[v] = tPitchShift_shiftToFreq(&r->ps[v], r->freq[v]); | |
| 1468 | + } | |
| 1469 | + | |
| 1470 | + return r->tickOutput; | |
| 1471 | +} | |
| 1472 | + | |
| 1473 | +void tAutotune_setNumVoices(tAutotune* const rt, int numVoices) | |
| 1474 | +{ | |
| 1475 | + _tAutotune* r = *rt; | |
| 1476 | + | |
| 1477 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1478 | + { | |
| 1479 | + tPitchShift_free(&r->ps[i]); | |
| 1480 | + leaf_free((char*)r->outBuffers[i]); | |
| 1481 | + } | |
| 1482 | + leaf_free((char*)r->tickOutput); | |
| 1483 | + leaf_free((char*)r->freq); | |
| 1484 | + leaf_free((char*)r->ps); | |
| 1485 | + leaf_free((char*)r->outBuffers); | |
| 1486 | + | |
| 1487 | + r->numVoices = numVoices; | |
| 1488 | + | |
| 1489 | + r->outBuffers = (float**) leaf_alloc(sizeof(float*) * r->numVoices); | |
| 1490 | + r->ps = (tPitchShift*) leaf_alloc(sizeof(tPitchShift) * r->numVoices); | |
| 1491 | + r->freq = (float*) leaf_alloc(sizeof(float) * r->numVoices); | |
| 1492 | + r->tickOutput = (float*) leaf_alloc(sizeof(float) * r->numVoices); | |
| 1493 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1494 | + { | |
| 1495 | + r->outBuffers[i] = (float*) leaf_alloc(sizeof(float) * r->bufSize); | |
| 1496 | + tPitchShift_init(&r->ps[i], &r->pd, r->outBuffers[i], r->bufSize); | |
| 1497 | + } | |
| 1498 | +} | |
| 1499 | + | |
| 1500 | +void tAutotune_setFreqs(tAutotune* const rt, float f) | |
| 1501 | +{ | |
| 1502 | + _tAutotune* r = *rt; | |
| 1503 | + | |
| 1504 | + for (int i = 0; i < r->numVoices; ++i) | |
| 1505 | + { | |
| 1506 | + r->freq[i] = f; | |
| 1507 | + } | |
| 1508 | +} | |
| 1509 | + | |
| 1510 | +void tAutotune_setFreq(tAutotune* const rt, float f, int voice) | |
| 1511 | +{ | |
| 1512 | + _tAutotune* r = *rt; | |
| 1513 | + | |
| 1514 | + r->freq[voice] = f; | |
| 1515 | +} | |
| 1516 | + | |
| 1517 | +void tAutotune_setTimeConstant(tAutotune* const rt, float tc) | |
| 1518 | +{ | |
| 1519 | + _tAutotune* r = *rt; | |
| 1520 | + | |
| 1521 | + r->timeConstant = tc; | |
| 1522 | + r->radius = expf(-1000.0f * r->hopSize * leaf.invSampleRate / r->timeConstant); | |
| 1523 | +} | |
| 1524 | + | |
| 1525 | +void tAutotune_setHopSize(tAutotune* const rt, int hs) | |
| 1526 | +{ | |
| 1527 | + _tAutotune* r = *rt; | |
| 1528 | + | |
| 1529 | + r->hopSize = hs; | |
| 1530 | + tPeriodDetection_setHopSize(&r->pd, r->hopSize); | |
| 1531 | +} | |
| 1532 | + | |
| 1533 | +void tAutotune_setWindowSize(tAutotune* const rt, int ws) | |
| 1534 | +{ | |
| 1535 | + _tAutotune* r = *rt; | |
| 1536 | + | |
| 1537 | + r->windowSize = ws; | |
| 1538 | + tPeriodDetection_setWindowSize(&r->pd, r->windowSize); | |
| 1539 | +} | |
| 1540 | + | |
| 1541 | +void tAutotune_setFidelityThreshold(tAutotune* const rt, float threshold) | |
| 1542 | +{ | |
| 1543 | + _tAutotune* r = *rt; | |
| 1544 | + | |
| 1545 | + tPeriodDetection_setFidelityThreshold(&r->pd, threshold); | |
| 1546 | +} | |
| 1547 | + | |
| 1548 | +void tAutotune_setAlpha (tAutotune* rt, float alpha) | |
| 1549 | +{ | |
| 1550 | + _tAutotune* r = *rt; | |
| 1551 | + tPeriodDetection_setAlpha(&r->pd, alpha); | |
| 1552 | +} | |
| 1553 | + | |
| 1554 | +void tAutotune_setTolerance (tAutotune* rt, float tolerance) | |
| 1555 | +{ | |
| 1556 | + _tAutotune* r = *rt; | |
| 1557 | + tPeriodDetection_setTolerance(&r->pd, tolerance); | |
| 1558 | +} | |
| 1559 | + | |
| 1560 | +float tAutotune_getInputPeriod(tAutotune* const rt) | |
| 1561 | +{ | |
| 1562 | + _tAutotune* r = *rt; | |
| 1563 | + | |
| 1564 | + return r->inputPeriod; | |
| 1565 | +} | |
| 1566 | + | |
| 1567 | +float tAutotune_getInputFreq(tAutotune* const rt) | |
| 1568 | +{ | |
| 1569 | + _tAutotune* r = *rt; | |
| 1570 | + | |
| 1571 | + return 1.0f/r->inputPeriod; | |
| 1572 | +} | |
| 1573 | + | |
| 1574 | +//============================================================================================================ | |
| 1575 | +// FORMANTSHIFTER | |
| 1576 | +//============================================================================================================ | |
| 1577 | +// algorithm from Tom Baran's autotalent code. | |
| 1578 | + | |
| 1579 | +void tFormantShifter_init(tFormantShifter* const fsr, int order) | |
| 1580 | +{ | |
| 1581 | + _tFormantShifter* fs = *fsr = (_tFormantShifter*) leaf_alloc(sizeof(_tFormantShifter)); | |
| 1582 | + | |
| 1583 | + fs->ford = order; | |
| 1584 | + fs->fk = (float*) leaf_calloc(sizeof(float) * fs->ford); | |
| 1585 | + fs->fb = (float*) leaf_calloc(sizeof(float) * fs->ford); | |
| 1586 | + fs->fc = (float*) leaf_calloc(sizeof(float) * fs->ford); | |
| 1587 | + fs->frb = (float*) leaf_calloc(sizeof(float) * fs->ford); | |
| 1588 | + fs->frc = (float*) leaf_calloc(sizeof(float) * fs->ford); | |
| 1589 | + fs->fsig = (float*) leaf_calloc(sizeof(float) * fs->ford); | |
| 1590 | + fs->fsmooth = (float*) leaf_calloc(sizeof(float) * fs->ford); | |
| 1591 | + fs->ftvec = (float*) leaf_calloc(sizeof(float) * fs->ford); | |
| 1592 | + fs->fbuff = (float*) leaf_calloc(sizeof(float*) * fs->ford); | |
| 1593 | + | |
| 1594 | + fs->falph = powf(0.001f, 40.0f * leaf.invSampleRate); | |
| 1595 | + fs->flamb = -(0.8517f*sqrtf(atanf(0.06583f*leaf.sampleRate))-0.1916f); | |
| 1596 | + fs->fhp = 0.0f; | |
| 1597 | + fs->flp = 0.0f; | |
| 1598 | + fs->flpa = powf(0.001f, 10.0f * leaf.invSampleRate); | |
| 1599 | + fs->fmute = 1.0f; | |
| 1600 | + fs->fmutealph = powf(0.001f, 0.5f * leaf.invSampleRate); | |
| 1601 | + fs->cbi = 0; | |
| 1602 | + fs->intensity = 1.0f; | |
| 1603 | + fs->invIntensity = 1.0f; | |
| 1604 | + tHighpass_init(&fs->hp, 10.0f); | |
| 1605 | + tHighpass_init(&fs->hp2, 10.0f); | |
| 1606 | + tFeedbackLeveler_init(&fs->fbl1, 0.99f, 0.005f, 0.125f, 0); | |
| 1607 | + tFeedbackLeveler_init(&fs->fbl2, 0.99f, 0.005f, 0.125f, 0); | |
| 1608 | +} | |
| 1609 | + | |
| 1610 | +void tFormantShifter_free(tFormantShifter* const fsr) | |
| 1611 | +{ | |
| 1612 | + _tFormantShifter* fs = *fsr; | |
| 1613 | + | |
| 1614 | + leaf_free((char*)fs->fk); | |
| 1615 | + leaf_free((char*)fs->fb); | |
| 1616 | + leaf_free((char*)fs->fc); | |
| 1617 | + leaf_free((char*)fs->frb); | |
| 1618 | + leaf_free((char*)fs->frc); | |
| 1619 | + leaf_free((char*)fs->fsig); | |
| 1620 | + leaf_free((char*)fs->fsmooth); | |
| 1621 | + leaf_free((char*)fs->ftvec); | |
| 1622 | + leaf_free((char*)fs->fbuff); | |
| 1623 | + tHighpass_free(&fs->hp); | |
| 1624 | + tHighpass_free(&fs->hp2); | |
| 1625 | + tFeedbackLeveler_free(&fs->fbl1); | |
| 1626 | + tFeedbackLeveler_free(&fs->fbl2); | |
| 1627 | + leaf_free((char*)fs); | |
| 1628 | +} | |
| 1629 | + | |
| 1630 | +void tFormantShifter_initToPool (tFormantShifter* const fsr, int order, tMempool* const mp) | |
| 1631 | +{ | |
| 1632 | + _tMempool* m = *mp; | |
| 1633 | + _tFormantShifter* fs = *fsr = (_tFormantShifter*) mpool_alloc(sizeof(_tFormantShifter), m); | |
| 1634 | + | |
| 1635 | + fs->ford = order; | |
| 1636 | + fs->fk = (float*) mpool_calloc(sizeof(float) * fs->ford, m); | |
| 1637 | + fs->fb = (float*) mpool_calloc(sizeof(float) * fs->ford, m); | |
| 1638 | + fs->fc = (float*) mpool_calloc(sizeof(float) * fs->ford, m); | |
| 1639 | + fs->frb = (float*) mpool_calloc(sizeof(float) * fs->ford, m); | |
| 1640 | + fs->frc = (float*) mpool_calloc(sizeof(float) * fs->ford, m); | |
| 1641 | + fs->fsig = (float*) mpool_calloc(sizeof(float) * fs->ford, m); | |
| 1642 | + fs->fsmooth = (float*) mpool_calloc(sizeof(float) * fs->ford, m); | |
| 1643 | + fs->ftvec = (float*) mpool_calloc(sizeof(float) * fs->ford, m); | |
| 1644 | + | |
| 1645 | + fs->fbuff = (float*) mpool_calloc(sizeof(float*) * fs->ford, m); | |
| 1646 | + | |
| 1647 | + | |
| 1648 | + fs->falph = powf(0.001f, 10.0f * leaf.invSampleRate); | |
| 1649 | + fs->flamb = -(0.8517f*sqrtf(atanf(0.06583f*leaf.sampleRate))-0.1916f); | |
| 1650 | + fs->fhp = 0.0f; | |
| 1651 | + fs->flp = 0.0f; | |
| 1652 | + fs->flpa = powf(0.001f, 10.0f * leaf.invSampleRate); | |
| 1653 | + fs->fmute = 1.0f; | |
| 1654 | + fs->fmutealph = powf(0.001f, 1.0f * leaf.invSampleRate); | |
| 1655 | + fs->cbi = 0; | |
| 1656 | + fs->intensity = 1.0f; | |
| 1657 | + fs->invIntensity = 1.0f; | |
| 1658 | + tHighpass_initToPool(&fs->hp, 20.0f, mp); | |
| 1659 | + tHighpass_initToPool(&fs->hp2, 20.0f, mp); | |
| 1660 | + tFeedbackLeveler_initToPool(&fs->fbl1, 0.8f, .005f, 0.125, 1, mp); | |
| 1661 | + tFeedbackLeveler_initToPool(&fs->fbl2, 0.8f, .005f, 0.125, 1, mp); | |
| 1662 | +} | |
| 1663 | + | |
| 1664 | +void tFormantShifter_freeFromPool (tFormantShifter* const fsr, tMempool* const mp) | |
| 1665 | +{ | |
| 1666 | + _tMempool* m = *mp; | |
| 1667 | + _tFormantShifter* fs = *fsr; | |
| 1668 | + | |
| 1669 | + mpool_free((char*)fs->fk, m); | |
| 1670 | + mpool_free((char*)fs->fb, m); | |
| 1671 | + mpool_free((char*)fs->fc, m); | |
| 1672 | + mpool_free((char*)fs->frb, m); | |
| 1673 | + mpool_free((char*)fs->frc, m); | |
| 1674 | + mpool_free((char*)fs->fsig, m); | |
| 1675 | + mpool_free((char*)fs->fsmooth, m); | |
| 1676 | + mpool_free((char*)fs->ftvec, m); | |
| 1677 | + mpool_free((char*)fs->fbuff, m); | |
| 1678 | + tHighpass_freeFromPool(&fs->hp, mp); | |
| 1679 | + tHighpass_freeFromPool(&fs->hp2, mp); | |
| 1680 | + tFeedbackLeveler_freeFromPool(&fs->fbl1, mp); | |
| 1681 | + tFeedbackLeveler_freeFromPool(&fs->fbl2, mp); | |
| 1682 | + mpool_free((char*)fs, m); | |
| 1683 | +} | |
| 1684 | + | |
| 1685 | +float tFormantShifter_tick(tFormantShifter* const fsr, float in) | |
| 1686 | +{ | |
| 1687 | + return tFormantShifter_add(fsr, tFormantShifter_remove(fsr, in)); | |
| 1688 | +} | |
| 1689 | + | |
| 1690 | +float tFormantShifter_remove(tFormantShifter* const fsr, float in) | |
| 1691 | +{ | |
| 1692 | + _tFormantShifter* fs = *fsr; | |
| 1693 | + in = tFeedbackLeveler_tick(&fs->fbl1, in); | |
| 1694 | + in = tHighpass_tick(&fs->hp, in * fs->intensity); | |
| 1695 | + | |
| 1696 | + | |
| 1697 | + float fa, fb, fc, foma, falph, ford, flamb, tf, fk; | |
| 1698 | + | |
| 1699 | + ford = fs->ford; | |
| 1700 | + falph = fs->falph; | |
| 1701 | + foma = (1.0f - falph); | |
| 1702 | + flamb = fs->flamb; | |
| 1703 | + | |
| 1704 | + tf = in; | |
| 1705 | + | |
| 1706 | + fa = tf - fs->fhp; | |
| 1707 | + fs->fhp = tf; | |
| 1708 | + fb = fa; | |
| 1709 | + for(int i = 0; i < ford; i++) | |
| 1710 | + { | |
| 1711 | + fs->fsig[i] = fa*fa*foma + fs->fsig[i]*falph; | |
| 1712 | + fc = (fb - fs->fc[i])*flamb + fs->fb[i]; | |
| 1713 | + fs->fc[i] = fc; | |
| 1714 | + fs->fb[i] = fb; | |
| 1715 | + fk = fa*fc*foma + fs->fk[i]*falph; | |
| 1716 | + fs->fk[i] = fk; | |
| 1717 | + tf = fk/(fs->fsig[i] + 0.000001f); | |
| 1718 | + tf = tf*foma + fs->fsmooth[i]*falph; | |
| 1719 | + fs->fsmooth[i] = tf; | |
| 1720 | + fs->fbuff[i] = tf; | |
| 1721 | + fb = fc - tf*fa; | |
| 1722 | + fa = fa - tf*fc; | |
| 1723 | + } | |
| 1724 | + | |
| 1725 | + //return fa * 0.1f; | |
| 1726 | + return fa; | |
| 1727 | +} | |
| 1728 | + | |
| 1729 | +float tFormantShifter_add(tFormantShifter* const fsr, float in) | |
| 1730 | +{ | |
| 1731 | + _tFormantShifter* fs = *fsr; | |
| 1732 | + | |
| 1733 | + float fa, fb, fc, ford, flpa, flamb, tf, tf2, f0resp, f1resp, frlamb; | |
| 1734 | + ford = fs->ford; | |
| 1735 | + | |
| 1736 | + flpa = fs->flpa; | |
| 1737 | + flamb = fs->flamb; | |
| 1738 | + tf = fs->shiftFactor * (1.0f+flamb)/(1.0f-flamb); | |
| 1739 | + frlamb = (tf-1.0f)/(tf+1.0f); | |
| 1740 | + | |
| 1741 | + tf2 = in; | |
| 1742 | + fa = 0.0f; | |
| 1743 | + fb = fa; | |
| 1744 | + for (int i=0; i<ford; i++) | |
| 1745 | + { | |
| 1746 | + fc = (fb-fs->frc[i])*frlamb + fs->frb[i]; | |
| 1747 | + tf = fs->fbuff[i]; | |
| 1748 | + fb = fc - tf*fa; | |
| 1749 | + fs->ftvec[i] = tf*fc; | |
| 1750 | + fa = fa - fs->ftvec[i]; | |
| 1751 | + } | |
| 1752 | + tf = -fa; | |
| 1753 | + for (int i=ford-1; i>=0; i--) | |
| 1754 | + { | |
| 1755 | + tf = tf + fs->ftvec[i]; | |
| 1756 | + } | |
| 1757 | + f0resp = tf; | |
| 1758 | + | |
| 1759 | + // second time: compute 1-response | |
| 1760 | + fa = 1.0f; | |
| 1761 | + fb = fa; | |
| 1762 | + for (int i=0; i<ford; i++) | |
| 1763 | + { | |
| 1764 | + fc = (fb-fs->frc[i])*frlamb + fs->frb[i]; | |
| 1765 | + tf = fs->fbuff[i]; | |
| 1766 | + fb = fc - tf*fa; | |
| 1767 | + fs->ftvec[i] = tf*fc; | |
| 1768 | + fa = fa - fs->ftvec[i]; | |
| 1769 | + } | |
| 1770 | + tf = -fa; | |
| 1771 | + for (int i=ford-1; i>=0; i--) | |
| 1772 | + { | |
| 1773 | + tf = tf + fs->ftvec[i]; | |
| 1774 | + } | |
| 1775 | + f1resp = tf; | |
| 1776 | + | |
| 1777 | + // now solve equations for output, based on 0-response and 1-response | |
| 1778 | + tf = 2.0f*tf2; | |
| 1779 | + tf2 = tf; | |
| 1780 | + tf = (1.0f - f1resp + f0resp); | |
| 1781 | + if (tf!=0.0f) | |
| 1782 | + { | |
| 1783 | + tf2 = (tf2 + f0resp) / tf; | |
| 1784 | + } | |
| 1785 | + else | |
| 1786 | + { | |
| 1787 | + tf2 = 0.0f; | |
| 1788 | + } | |
| 1789 | + | |
| 1790 | + // third time: update delay registers | |
| 1791 | + fa = tf2; | |
| 1792 | + fb = fa; | |
| 1793 | + for (int i=0; i<ford; i++) | |
| 1794 | + { | |
| 1795 | + fc = (fb-fs->frc[i])*frlamb + fs->frb[i]; | |
| 1796 | + fs->frc[i] = fc; | |
| 1797 | + fs->frb[i] = fb; | |
| 1798 | + tf = fs->fbuff[i]; | |
| 1799 | + fb = fc - tf*fa; | |
| 1800 | + fa = fa - tf*fc; | |
| 1801 | + } | |
| 1802 | + tf = tf2; | |
| 1803 | + tf = tf + flpa * fs->flp; // lowpass post-emphasis filter | |
| 1804 | + fs->flp = tf; | |
| 1805 | + | |
| 1806 | + // Bring up the gain slowly when formant correction goes from disabled | |
| 1807 | + // to enabled, while things stabilize. | |
| 1808 | + if (fs->fmute>0.5f) | |
| 1809 | + { | |
| 1810 | + tf = tf*(fs->fmute - 0.5f)*2.0f; | |
| 1811 | + } | |
| 1812 | + else | |
| 1813 | + { | |
| 1814 | + tf = 0.0f; | |
| 1815 | + } | |
| 1816 | + tf2 = fs->fmutealph; | |
| 1817 | + fs->fmute = (1.0f-tf2) + tf2*fs->fmute; | |
| 1818 | + // now tf is signal output | |
| 1819 | + // ...and we're done messing with formants | |
| 1820 | + //tf = tFeedbackLeveler_tick(&fs->fbl2, tf); | |
| 1821 | + tf = tHighpass_tick(&fs->hp2, tanhf(tf)); | |
| 1822 | + | |
| 1823 | + return tf * fs->invIntensity; | |
| 1824 | +} | |
| 1825 | + | |
| 1826 | +// 1.0f is no change, 2.0f is an octave up, 0.5f is an octave down | |
| 1827 | +void tFormantShifter_setShiftFactor(tFormantShifter* const fsr, float shiftFactor) | |
| 1828 | +{ | |
| 1829 | + _tFormantShifter* fs = *fsr; | |
| 1830 | + fs->shiftFactor = shiftFactor; | |
| 1831 | +} | |
| 1832 | + | |
| 1833 | +void tFormantShifter_setIntensity(tFormantShifter* const fsr, float intensity) | |
| 1834 | +{ | |
| 1835 | + _tFormantShifter* fs = *fsr; | |
| 1836 | + | |
| 1837 | + | |
| 1838 | + | |
| 1839 | + fs->intensity = LEAF_clip(1.0f, intensity, 100.0f); | |
| 1840 | + | |
| 1841 | + // tFeedbackLeveler_setTargetLevel(&fs->fbl1, fs->intensity); | |
| 1842 | + //tFeedbackLeveler_setTargetLevel(&fs->fbl2, fs->intensity); | |
| 1843 | + //make sure you don't divide by zero, doofies | |
| 1844 | + if (fs->intensity != 0.0f) | |
| 1845 | + { | |
| 1846 | + fs->invIntensity = 1.0f/fs->intensity; | |
| 1847 | + } | |
| 1848 | + else | |
| 1849 | + { | |
| 1850 | + fs->invIntensity = 1.0f; | |
| 1851 | + } | |
| 1852 | + | |
| 1853 | +} | |
| @@ -0,0 +1,551 @@ | ||
| 1 | +/* | |
| 2 | + * leaf-electrical.c | |
| 3 | + * | |
| 4 | + * Created on: Sep 25, 2019 | |
| 5 | + * Author: jeffsnyder | |
| 6 | + */ | |
| 7 | + | |
| 8 | +#if _WIN32 || _WIN64 | |
| 9 | + | |
| 10 | +#include "..\Inc\leaf-electrical.h" | |
| 11 | +#include "..\leaf.h" | |
| 12 | + | |
| 13 | +#else | |
| 14 | + | |
| 15 | +#include "../Inc/leaf-electrical.h" | |
| 16 | +#include "../leaf.h" | |
| 17 | + | |
| 18 | +#endif | |
| 19 | + | |
| 20 | +//============================================================================== | |
| 21 | + | |
| 22 | +static float get_port_resistance_for_resistor(tWDF* const r); | |
| 23 | +static float get_port_resistance_for_capacitor(tWDF* const r); | |
| 24 | +static float get_port_resistance_for_inductor(tWDF* const r); | |
| 25 | +static float get_port_resistance_for_resistive(tWDF* const r); | |
| 26 | +static float get_port_resistance_for_inverter(tWDF* const r); | |
| 27 | +static float get_port_resistance_for_series(tWDF* const r); | |
| 28 | +static float get_port_resistance_for_parallel(tWDF* const r); | |
| 29 | +static float get_port_resistance_for_root(tWDF* const r); | |
| 30 | + | |
| 31 | +static void set_incident_wave_for_leaf(tWDF* const r, float incident_wave, float input); | |
| 32 | +static void set_incident_wave_for_leaf_inverted(tWDF* const r, float incident_wave, float input); | |
| 33 | +static void set_incident_wave_for_inverter(tWDF* const r, float incident_wave, float input); | |
| 34 | +static void set_incident_wave_for_series(tWDF* const r, float incident_wave, float input); | |
| 35 | +static void set_incident_wave_for_parallel(tWDF* const r, float incident_wave, float input); | |
| 36 | + | |
| 37 | +static float get_reflected_wave_for_resistor(tWDF* const r, float input); | |
| 38 | +static float get_reflected_wave_for_capacitor(tWDF* const r, float input); | |
| 39 | +static float get_reflected_wave_for_resistive(tWDF* const r, float input); | |
| 40 | +static float get_reflected_wave_for_inverter(tWDF* const r, float input); | |
| 41 | +static float get_reflected_wave_for_series(tWDF* const r, float input); | |
| 42 | +static float get_reflected_wave_for_parallel(tWDF* const r, float input); | |
| 43 | + | |
| 44 | +static float get_reflected_wave_for_ideal(tWDF* const n, float input, float incident_wave); | |
| 45 | +static float get_reflected_wave_for_diode(tWDF* const n, float input, float incident_wave); | |
| 46 | +static float get_reflected_wave_for_diode_pair(tWDF* const n, float input, float incident_wave); | |
| 47 | + | |
| 48 | +static void wdf_init(tWDF* const wdf, WDFComponentType type, float value, tWDF* const rL, tWDF* const rR) | |
| 49 | +{ | |
| 50 | + _tWDF* r = *wdf; | |
| 51 | + | |
| 52 | + r->type = type; | |
| 53 | + r->child_left = rL; | |
| 54 | + r->child_right = rR; | |
| 55 | + r->incident_wave_up = 0.0f; | |
| 56 | + r->incident_wave_left = 0.0f; | |
| 57 | + r->incident_wave_right = 0.0f; | |
| 58 | + r->reflected_wave_up = 0.0f; | |
| 59 | + r->reflected_wave_left = 0.0f; | |
| 60 | + r->reflected_wave_right = 0.0f; | |
| 61 | + r->sample_rate = leaf.sampleRate; | |
| 62 | + r->value = value; | |
| 63 | + | |
| 64 | + tWDF* child; | |
| 65 | + if (r->child_left != NULL) child = r->child_left; | |
| 66 | + else child = r->child_right; | |
| 67 | + | |
| 68 | + if (r->type == Resistor) | |
| 69 | + { | |
| 70 | + r->port_resistance_up = r->value; | |
| 71 | + r->port_conductance_up = 1.0f / r->value; | |
| 72 | + | |
| 73 | + r->get_port_resistance = &get_port_resistance_for_resistor; | |
| 74 | + r->get_reflected_wave_up = &get_reflected_wave_for_resistor; | |
| 75 | + r->set_incident_wave = &set_incident_wave_for_leaf; | |
| 76 | + } | |
| 77 | + else if (r->type == Capacitor) | |
| 78 | + { | |
| 79 | + r->port_conductance_up = r->sample_rate * 2.0f * r->value; | |
| 80 | + r->port_resistance_up = 1.0f / r->port_conductance_up; //based on trapezoidal discretization | |
| 81 | + | |
| 82 | + r->get_port_resistance = &get_port_resistance_for_capacitor; | |
| 83 | + r->get_reflected_wave_up = &get_reflected_wave_for_capacitor; | |
| 84 | + r->set_incident_wave = &set_incident_wave_for_leaf; | |
| 85 | + } | |
| 86 | + else if (r->type == Inductor) | |
| 87 | + { | |
| 88 | + r->port_resistance_up = r->sample_rate * 2.0f * r->value; //based on trapezoidal discretization | |
| 89 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 90 | + | |
| 91 | + r->get_port_resistance = &get_port_resistance_for_inductor; | |
| 92 | + r->get_reflected_wave_up = &get_reflected_wave_for_capacitor; // same as capacitor | |
| 93 | + r->set_incident_wave = &set_incident_wave_for_leaf_inverted; | |
| 94 | + } | |
| 95 | + else if (r->type == ResistiveSource) | |
| 96 | + { | |
| 97 | + r->port_resistance_up = r->value; | |
| 98 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 99 | + | |
| 100 | + r->get_port_resistance = &get_port_resistance_for_resistive; | |
| 101 | + r->get_reflected_wave_up = &get_reflected_wave_for_resistive; | |
| 102 | + r->set_incident_wave = &set_incident_wave_for_leaf; | |
| 103 | + } | |
| 104 | + else if (r->type == Inverter) | |
| 105 | + { | |
| 106 | + r->port_resistance_up = tWDF_getPortResistance(r->child_left); | |
| 107 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 108 | + | |
| 109 | + r->get_port_resistance = &get_port_resistance_for_inverter; | |
| 110 | + r->get_reflected_wave_up = &get_reflected_wave_for_inverter; | |
| 111 | + r->set_incident_wave = &set_incident_wave_for_inverter; | |
| 112 | + } | |
| 113 | + else if (r->type == SeriesAdaptor) | |
| 114 | + { | |
| 115 | + r->port_resistance_left = tWDF_getPortResistance(r->child_left); | |
| 116 | + r->port_resistance_right = tWDF_getPortResistance(r->child_right); | |
| 117 | + r->port_resistance_up = r->port_resistance_left + r->port_resistance_right; | |
| 118 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 119 | + r->port_conductance_left = 1.0f / r->port_resistance_left; | |
| 120 | + r->port_conductance_right = 1.0f / r->port_resistance_right; | |
| 121 | + r->gamma_zero = 1.0f / (r->port_resistance_right + r->port_resistance_left); | |
| 122 | + | |
| 123 | + r->get_port_resistance = &get_port_resistance_for_series; | |
| 124 | + r->get_reflected_wave_up = &get_reflected_wave_for_series; | |
| 125 | + r->set_incident_wave = &set_incident_wave_for_series; | |
| 126 | + } | |
| 127 | + else if (r->type == ParallelAdaptor) | |
| 128 | + { | |
| 129 | + r->port_resistance_left = tWDF_getPortResistance(r->child_left); | |
| 130 | + r->port_resistance_right = tWDF_getPortResistance(r->child_right); | |
| 131 | + r->port_resistance_up = (r->port_resistance_left * r->port_resistance_right) / (r->port_resistance_left + r->port_resistance_right); | |
| 132 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 133 | + r->port_conductance_left = 1.0f / r->port_resistance_left; | |
| 134 | + r->port_conductance_right = 1.0f / r->port_resistance_right; | |
| 135 | + r->gamma_zero = 1.0f / (r->port_resistance_right + r->port_resistance_left); | |
| 136 | + | |
| 137 | + r->get_port_resistance = &get_port_resistance_for_parallel; | |
| 138 | + r->get_reflected_wave_up = &get_reflected_wave_for_parallel; | |
| 139 | + r->set_incident_wave = &set_incident_wave_for_parallel; | |
| 140 | + } | |
| 141 | + else if (r->type == IdealSource) | |
| 142 | + { | |
| 143 | + r->port_resistance_up = tWDF_getPortResistance(child); | |
| 144 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 145 | + | |
| 146 | + r->get_reflected_wave_down = &get_reflected_wave_for_ideal; | |
| 147 | + r->get_port_resistance = &get_port_resistance_for_root; | |
| 148 | + } | |
| 149 | + else if (r->type == Diode) | |
| 150 | + { | |
| 151 | + r->port_resistance_up = tWDF_getPortResistance(child); | |
| 152 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 153 | + | |
| 154 | + r->get_reflected_wave_down = &get_reflected_wave_for_diode; | |
| 155 | + r->get_port_resistance = &get_port_resistance_for_root; | |
| 156 | + } | |
| 157 | + else if (r->type == DiodePair) | |
| 158 | + { | |
| 159 | + r->port_resistance_up = tWDF_getPortResistance(child); | |
| 160 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 161 | + | |
| 162 | + r->get_reflected_wave_down = &get_reflected_wave_for_diode_pair; | |
| 163 | + r->get_port_resistance = &get_port_resistance_for_root; | |
| 164 | + } | |
| 165 | +} | |
| 166 | +//WDF | |
| 167 | +void tWDF_init(tWDF* const wdf, WDFComponentType type, float value, tWDF* const rL, tWDF* const rR) | |
| 168 | +{ | |
| 169 | + *wdf = (_tWDF*) leaf_alloc(sizeof(_tWDF)); | |
| 170 | + | |
| 171 | + wdf_init(wdf, type, value, rL, rR); | |
| 172 | +} | |
| 173 | + | |
| 174 | +void tWDF_free(tWDF* const wdf) | |
| 175 | +{ | |
| 176 | + _tWDF* r = *wdf; | |
| 177 | + | |
| 178 | + leaf_free((char*)r); | |
| 179 | +} | |
| 180 | + | |
| 181 | +void tWDF_initToPool (tWDF* const wdf, WDFComponentType type, float value, tWDF* const rL, tWDF* const rR, tMempool* const mp) | |
| 182 | +{ | |
| 183 | + _tMempool* m = *mp; | |
| 184 | + *wdf = (_tWDF*) mpool_alloc(sizeof(_tWDF), m); | |
| 185 | + | |
| 186 | + wdf_init(wdf, type, value, rL, rR); | |
| 187 | +} | |
| 188 | + | |
| 189 | +void tWDF_freeFromPool (tWDF* const wdf, tMempool* const mp) | |
| 190 | +{ | |
| 191 | + _tMempool* m = *mp; | |
| 192 | + _tWDF* r = *wdf; | |
| 193 | + | |
| 194 | + mpool_free((char*)r, m); | |
| 195 | +} | |
| 196 | + | |
| 197 | +float tWDF_tick(tWDF* const wdf, float sample, tWDF* const outputPoint, uint8_t paramsChanged) | |
| 198 | +{ | |
| 199 | + _tWDF* r = *wdf; | |
| 200 | + | |
| 201 | + tWDF* child; | |
| 202 | + if (r->child_left != NULL) child = r->child_left; | |
| 203 | + else child = r->child_right; | |
| 204 | + | |
| 205 | + //step 0 : update port resistances if something changed | |
| 206 | + if (paramsChanged) tWDF_getPortResistance(wdf); | |
| 207 | + | |
| 208 | + //step 1 : set inputs to what they should be | |
| 209 | + float input = sample; | |
| 210 | + | |
| 211 | + //step 2 : scan the waves up the tree | |
| 212 | + r->incident_wave_up = tWDF_getReflectedWaveUp(child, input); | |
| 213 | + | |
| 214 | + //step 3 : do root scattering computation | |
| 215 | + r->reflected_wave_up = tWDF_getReflectedWaveDown(wdf, input, r->incident_wave_up); | |
| 216 | + | |
| 217 | + //step 4 : propogate waves down the tree | |
| 218 | + tWDF_setIncidentWave(child, r->reflected_wave_up, input); | |
| 219 | + | |
| 220 | + //step 5 : grab whatever voltages or currents we want as outputs | |
| 221 | + return tWDF_getVoltage(outputPoint); | |
| 222 | +} | |
| 223 | + | |
| 224 | +void tWDF_setValue(tWDF* const wdf, float value) | |
| 225 | +{ | |
| 226 | + _tWDF* r = *wdf; | |
| 227 | + r->value = value; | |
| 228 | +} | |
| 229 | + | |
| 230 | +void tWDF_setSampleRate(tWDF* const wdf, float sample_rate) | |
| 231 | +{ | |
| 232 | + _tWDF* r = *wdf; | |
| 233 | + r->sample_rate = sample_rate; | |
| 234 | +} | |
| 235 | + | |
| 236 | +uint8_t tWDF_isLeaf(tWDF* const wdf) | |
| 237 | +{ | |
| 238 | + _tWDF* r = *wdf; | |
| 239 | + if (r->child_left == NULL && r->child_right == NULL) return 1; | |
| 240 | + return 0; | |
| 241 | +} | |
| 242 | + | |
| 243 | +float tWDF_getPortResistance(tWDF* const wdf) | |
| 244 | +{ | |
| 245 | + _tWDF* r = *wdf; | |
| 246 | + return r->get_port_resistance(wdf); | |
| 247 | +} | |
| 248 | + | |
| 249 | +void tWDF_setIncidentWave(tWDF* const wdf, float incident_wave, float input) | |
| 250 | +{ | |
| 251 | + _tWDF* r = *wdf; | |
| 252 | + r->set_incident_wave(wdf, incident_wave, input); | |
| 253 | +} | |
| 254 | + | |
| 255 | +float tWDF_getReflectedWaveUp(tWDF* const wdf, float input) | |
| 256 | +{ | |
| 257 | + _tWDF* r = *wdf; | |
| 258 | + return r->get_reflected_wave_up(wdf, input); | |
| 259 | +} | |
| 260 | + | |
| 261 | +float tWDF_getReflectedWaveDown(tWDF* const wdf, float input, float incident_wave) | |
| 262 | +{ | |
| 263 | + _tWDF* r = *wdf; | |
| 264 | + return r->get_reflected_wave_down(wdf, input, incident_wave); | |
| 265 | +} | |
| 266 | + | |
| 267 | +float tWDF_getVoltage(tWDF* const wdf) | |
| 268 | +{ | |
| 269 | + _tWDF* r = *wdf; | |
| 270 | + return ((r->incident_wave_up * 0.5f) + (r->reflected_wave_up * 0.5f)); | |
| 271 | +} | |
| 272 | + | |
| 273 | +float tWDF_getCurrent(tWDF* const wdf) | |
| 274 | +{ | |
| 275 | + _tWDF* r = *wdf; | |
| 276 | + return (((r->incident_wave_up * 0.5f) - (r->reflected_wave_up * 0.5f)) * r->port_conductance_up); | |
| 277 | +} | |
| 278 | + | |
| 279 | +//============ Static Functions to be Pointed To ==================== | |
| 280 | +//=================================================================== | |
| 281 | +//============ Get and Calculate Port Resistances =================== | |
| 282 | + | |
| 283 | +static float get_port_resistance_for_resistor(tWDF* const wdf) | |
| 284 | +{ | |
| 285 | + _tWDF* r = *wdf; | |
| 286 | + | |
| 287 | + r->port_resistance_up = r->value; | |
| 288 | + r->port_conductance_up = 1.0f / r->value; | |
| 289 | + | |
| 290 | + return r->port_resistance_up; | |
| 291 | +} | |
| 292 | + | |
| 293 | +static float get_port_resistance_for_capacitor(tWDF* const wdf) | |
| 294 | +{ | |
| 295 | + _tWDF* r = *wdf; | |
| 296 | + | |
| 297 | + r->port_conductance_up = r->sample_rate * 2.0f * r->value; //based on trapezoidal discretization | |
| 298 | + r->port_resistance_up = (1.0f / r->port_conductance_up); | |
| 299 | + | |
| 300 | + return r->port_resistance_up; | |
| 301 | +} | |
| 302 | + | |
| 303 | +static float get_port_resistance_for_inductor(tWDF* const wdf) | |
| 304 | +{ | |
| 305 | + _tWDF* r = *wdf; | |
| 306 | + | |
| 307 | + r->port_resistance_up = r->sample_rate * 2.0f * r->value; //based on trapezoidal discretization | |
| 308 | + r->port_conductance_up = (1.0f / r->port_resistance_up); | |
| 309 | + | |
| 310 | + return r->port_resistance_up; | |
| 311 | +} | |
| 312 | + | |
| 313 | +static float get_port_resistance_for_resistive(tWDF* const wdf) | |
| 314 | +{ | |
| 315 | + _tWDF* r = *wdf; | |
| 316 | + | |
| 317 | + r->port_resistance_up = r->value; | |
| 318 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 319 | + | |
| 320 | + return r->port_resistance_up; | |
| 321 | +} | |
| 322 | + | |
| 323 | +static float get_port_resistance_for_inverter(tWDF* const wdf) | |
| 324 | +{ | |
| 325 | + _tWDF* r = *wdf; | |
| 326 | + | |
| 327 | + r->port_resistance_up = tWDF_getPortResistance(r->child_left); | |
| 328 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 329 | + | |
| 330 | + return r->port_resistance_up; | |
| 331 | +} | |
| 332 | + | |
| 333 | +static float get_port_resistance_for_series(tWDF* const wdf) | |
| 334 | +{ | |
| 335 | + _tWDF* r = *wdf; | |
| 336 | + | |
| 337 | + r->port_resistance_left = tWDF_getPortResistance(r->child_left); | |
| 338 | + r->port_resistance_right = tWDF_getPortResistance(r->child_right); | |
| 339 | + r->port_resistance_up = r->port_resistance_left + r->port_resistance_right; | |
| 340 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 341 | + r->port_conductance_left = 1.0f / r->port_resistance_left; | |
| 342 | + r->port_conductance_right = 1.0f / r->port_resistance_right; | |
| 343 | + r->gamma_zero = 1.0f / (r->port_resistance_right + r->port_resistance_left); | |
| 344 | + | |
| 345 | + return r->port_resistance_up; | |
| 346 | +} | |
| 347 | + | |
| 348 | +static float get_port_resistance_for_parallel(tWDF* const wdf) | |
| 349 | +{ | |
| 350 | + _tWDF* r = *wdf; | |
| 351 | + | |
| 352 | + r->port_resistance_left = tWDF_getPortResistance(r->child_left); | |
| 353 | + r->port_resistance_right = tWDF_getPortResistance(r->child_right); | |
| 354 | + r->port_resistance_up = (r->port_resistance_left * r->port_resistance_right) / (r->port_resistance_left + r->port_resistance_right); | |
| 355 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 356 | + r->port_conductance_left = 1.0f / r->port_resistance_left; | |
| 357 | + r->port_conductance_right = 1.0f / r->port_resistance_right; | |
| 358 | + r->gamma_zero = 1.0f / (r->port_conductance_right + r->port_conductance_left); | |
| 359 | + | |
| 360 | + return r->port_resistance_up; | |
| 361 | +} | |
| 362 | + | |
| 363 | +static float get_port_resistance_for_root(tWDF* const wdf) | |
| 364 | +{ | |
| 365 | + _tWDF* r = *wdf; | |
| 366 | + | |
| 367 | + tWDF* child; | |
| 368 | + if (r->child_left != NULL) child = r->child_left; | |
| 369 | + else child = r->child_right; | |
| 370 | + | |
| 371 | + r->port_resistance_up = tWDF_getPortResistance(child); | |
| 372 | + r->port_conductance_up = 1.0f / r->port_resistance_up; | |
| 373 | + | |
| 374 | + return r->port_resistance_up; | |
| 375 | +} | |
| 376 | + | |
| 377 | +//=================================================================== | |
| 378 | +//================ Set Incident Waves =============================== | |
| 379 | + | |
| 380 | +static void set_incident_wave_for_leaf(tWDF* const wdf, float incident_wave, float input) | |
| 381 | +{ | |
| 382 | + _tWDF* r = *wdf; | |
| 383 | + r->incident_wave_up = incident_wave; | |
| 384 | +} | |
| 385 | + | |
| 386 | +static void set_incident_wave_for_leaf_inverted(tWDF* const wdf, float incident_wave, float input) | |
| 387 | +{ | |
| 388 | + _tWDF* r = *wdf; | |
| 389 | + r->incident_wave_up = -1.0f * incident_wave; | |
| 390 | +} | |
| 391 | + | |
| 392 | +static void set_incident_wave_for_inverter(tWDF* const wdf, float incident_wave, float input) | |
| 393 | +{ | |
| 394 | + _tWDF* r = *wdf; | |
| 395 | + r->incident_wave_up = incident_wave; | |
| 396 | + tWDF_setIncidentWave(r->child_left, -1.0f * incident_wave, input); | |
| 397 | +} | |
| 398 | + | |
| 399 | +static void set_incident_wave_for_series(tWDF* const wdf, float incident_wave, float input) | |
| 400 | +{ | |
| 401 | + _tWDF* r = *wdf; | |
| 402 | + | |
| 403 | + r->incident_wave_up = incident_wave; | |
| 404 | + float gamma_left = r->port_resistance_left * r->gamma_zero; | |
| 405 | + float gamma_right = r->port_resistance_right * r->gamma_zero; | |
| 406 | + float left_wave = tWDF_getReflectedWaveUp(r->child_left, input); | |
| 407 | + float right_wave = tWDF_getReflectedWaveUp(r->child_right, input); | |
| 408 | +// downPorts[0]->b = yl * ( downPorts[0]->a * ((1.0 / yl) - 1) - downPorts[1]->a - descendingWave ); | |
| 409 | +// downPorts[1]->b = yr * ( downPorts[1]->a * ((1.0 / yr) - 1) - downPorts[0]->a - descendingWave ); | |
| 410 | + tWDF_setIncidentWave(r->child_left, (-1.0f * gamma_left * incident_wave) + (gamma_right * left_wave) - (gamma_left * right_wave), input); | |
| 411 | + tWDF_setIncidentWave(r->child_right, (-1.0f * gamma_right * incident_wave) + (gamma_left * right_wave) - (gamma_right * left_wave), input); | |
| 412 | + // From rt-wdf | |
| 413 | +// tWDF_setIncidentWave(r->child_left, gamma_left * (left_wave * ((1.0f / gamma_left) - 1.0f) - right_wave - incident_wave)); | |
| 414 | +// tWDF_setIncidentWave(r->child_right, gamma_right * (right_wave * ((1.0f / gamma_right) - 1.0f) - left_wave - incident_wave)); | |
| 415 | + | |
| 416 | +} | |
| 417 | + | |
| 418 | +static void set_incident_wave_for_parallel(tWDF* const wdf, float incident_wave, float input) | |
| 419 | +{ | |
| 420 | + _tWDF* r = *wdf; | |
| 421 | + | |
| 422 | + r->incident_wave_up = incident_wave; | |
| 423 | + float gamma_left = r->port_conductance_left * r->gamma_zero; | |
| 424 | + float gamma_right = r->port_conductance_right * r->gamma_zero; | |
| 425 | + float left_wave = tWDF_getReflectedWaveUp(r->child_left, input); | |
| 426 | + float right_wave = tWDF_getReflectedWaveUp(r->child_right, input); | |
| 427 | +// downPorts[0]->b = ( ( dl - 1 ) * downPorts[0]->a + dr * downPorts[1]->a + du * descendingWave ); | |
| 428 | +// downPorts[1]->b = ( dl * downPorts[0]->a + ( dr - 1 ) * downPorts[1]->a + du * descendingWave ); | |
| 429 | + tWDF_setIncidentWave(r->child_left, (gamma_left - 1.0f) * left_wave + gamma_right * right_wave + incident_wave, input); | |
| 430 | + tWDF_setIncidentWave(r->child_right, gamma_left * left_wave + (gamma_right - 1.0f) * right_wave + incident_wave, input); | |
| 431 | +} | |
| 432 | + | |
| 433 | +//=================================================================== | |
| 434 | +//================ Get Reflected Waves ============================== | |
| 435 | + | |
| 436 | +static float get_reflected_wave_for_resistor(tWDF* const wdf, float input) | |
| 437 | +{ | |
| 438 | + _tWDF* r = *wdf; | |
| 439 | + r->reflected_wave_up = 0.0f; | |
| 440 | + return r->reflected_wave_up; | |
| 441 | +} | |
| 442 | + | |
| 443 | +static float get_reflected_wave_for_capacitor(tWDF* const wdf, float input) | |
| 444 | +{ | |
| 445 | + _tWDF* r = *wdf; | |
| 446 | + r->reflected_wave_up = r->incident_wave_up; | |
| 447 | + return r->reflected_wave_up; | |
| 448 | +} | |
| 449 | + | |
| 450 | +static float get_reflected_wave_for_resistive(tWDF* const wdf, float input) | |
| 451 | +{ | |
| 452 | + _tWDF* r = *wdf; | |
| 453 | + r->reflected_wave_up = input; | |
| 454 | + return r->reflected_wave_up; | |
| 455 | +} | |
| 456 | + | |
| 457 | +static float get_reflected_wave_for_inverter(tWDF* const wdf, float input) | |
| 458 | +{ | |
| 459 | + _tWDF* r = *wdf; | |
| 460 | + r->reflected_wave_up = -1.0f * tWDF_getReflectedWaveUp(r->child_left, input); | |
| 461 | + return r->reflected_wave_up; | |
| 462 | +} | |
| 463 | + | |
| 464 | +static float get_reflected_wave_for_series(tWDF* const wdf, float input) | |
| 465 | +{ | |
| 466 | + _tWDF* r = *wdf; | |
| 467 | + //-( downPorts[0]->a + downPorts[1]->a ); | |
| 468 | + r->reflected_wave_up = (-1.0f * (tWDF_getReflectedWaveUp(r->child_left, input) + tWDF_getReflectedWaveUp(r->child_right, input))); | |
| 469 | + return r->reflected_wave_up; | |
| 470 | +} | |
| 471 | + | |
| 472 | +static float get_reflected_wave_for_parallel(tWDF* const wdf, float input) | |
| 473 | +{ | |
| 474 | + _tWDF* r = *wdf; | |
| 475 | + | |
| 476 | + float gamma_left = r->port_conductance_left * r->gamma_zero; | |
| 477 | + float gamma_right = r->port_conductance_right * r->gamma_zero; | |
| 478 | + //return ( dl * downPorts[0]->a + dr * downPorts[1]->a ); | |
| 479 | + r->reflected_wave_up = (gamma_left * tWDF_getReflectedWaveUp(r->child_left, input) + gamma_right * tWDF_getReflectedWaveUp(r->child_right, input)); | |
| 480 | + return r->reflected_wave_up; | |
| 481 | +} | |
| 482 | + | |
| 483 | +static float get_reflected_wave_for_ideal(tWDF* const wdf, float input, float incident_wave) | |
| 484 | +{ | |
| 485 | + return (2.0f * input) - incident_wave; | |
| 486 | +} | |
| 487 | + | |
| 488 | +#define l2A 0.1640425613334452f | |
| 489 | +#define l2B -1.098865286222744f | |
| 490 | +#define l2Y 3.148297929334117f | |
| 491 | +#define l2K -2.213475204444817f | |
| 492 | +static float log2Approximation(float x) | |
| 493 | +{ | |
| 494 | + return (l2A * x*x*x) + (l2B * x*x) + (l2Y * x) + l2K; | |
| 495 | +} | |
| 496 | + | |
| 497 | +#define wX1 -3.684303659906469f | |
| 498 | +#define wX2 1.972967391708859f | |
| 499 | +#define wA 9.451797158780131e-3f | |
| 500 | +#define wB 0.1126446405111627f | |
| 501 | +#define wY 0.4451353886588814f | |
| 502 | +#define wK 0.5836596684310648f | |
| 503 | +static float wrightOmega3(float x) | |
| 504 | +{ | |
| 505 | + if (x <= wX1) | |
| 506 | + { | |
| 507 | + return 0; | |
| 508 | + } | |
| 509 | + else if (x < wX2) | |
| 510 | + { | |
| 511 | + return (wA * x*x*x) + (wB * x*x) + (wY * x) + wK; | |
| 512 | + } | |
| 513 | + else | |
| 514 | + { | |
| 515 | + return x - logf(x); | |
| 516 | + } | |
| 517 | +} | |
| 518 | + | |
| 519 | +static float wrightOmegaApproximation(float x) | |
| 520 | +{ | |
| 521 | + float w3 = wrightOmega3(x); | |
| 522 | + return w3 - ((w3 - expf(x - w3)) / (w3 + 1.0f)); | |
| 523 | +} | |
| 524 | + | |
| 525 | +static float lambertW(float a, float r, float I, float iVT) | |
| 526 | +{ | |
| 527 | + return wrightOmegaApproximation(((a + r*I) * iVT) + log((r * I) * iVT)); | |
| 528 | +} | |
| 529 | + | |
| 530 | +#define Is_DIODE 2.52e-9f | |
| 531 | +#define VT_DIODE 0.02585f | |
| 532 | +static float get_reflected_wave_for_diode(tWDF* const wdf, float input, float incident_wave) | |
| 533 | +{ | |
| 534 | + _tWDF* n = *wdf; | |
| 535 | + | |
| 536 | + float a = incident_wave; | |
| 537 | + float r = n->port_resistance_up; | |
| 538 | + return a + 2.0f*r*Is_DIODE - 2.0f*VT_DIODE*lambertW(a, r, Is_DIODE, 1.0f/VT_DIODE); | |
| 539 | +} | |
| 540 | + | |
| 541 | +static float get_reflected_wave_for_diode_pair(tWDF* const wdf, float input, float incident_wave) | |
| 542 | +{ | |
| 543 | + _tWDF* n = *wdf; | |
| 544 | + | |
| 545 | + float a = incident_wave; | |
| 546 | + float sgn = 0.0f; | |
| 547 | + if (a > 0.0f) sgn = 1.0f; | |
| 548 | + else if (a < 0.0f) sgn = -1.0f; | |
| 549 | + float r = n->port_resistance_up; | |
| 550 | + return a + 2 * sgn * (r*Is_DIODE - VT_DIODE*lambertW(sgn*a, r, Is_DIODE, 1.0f/VT_DIODE)); | |
| 551 | +} | |
| @@ -0,0 +1,1612 @@ | ||
| 1 | +/* | |
| 2 | + ============================================================================== | |
| 3 | + | |
| 4 | + leaf-envelopes.c | |
| 5 | + Created: 20 Jan 2017 12:02:17pm | |
| 6 | + Author: Michael R Mulshine | |
| 7 | + | |
| 8 | + ============================================================================== | |
| 9 | +*/ | |
| 10 | + | |
| 11 | + | |
| 12 | +#if _WIN32 || _WIN64 | |
| 13 | + | |
| 14 | +#include "..\Inc\leaf-envelopes.h" | |
| 15 | +#include "..\Inc\leaf-tables.h" | |
| 16 | +#include "..\leaf.h" | |
| 17 | + | |
| 18 | +#else | |
| 19 | + | |
| 20 | +#include "../Inc/leaf-envelopes.h" | |
| 21 | +#include "../Inc/leaf-tables.h" | |
| 22 | +#include "../leaf.h" | |
| 23 | + | |
| 24 | +#endif | |
| 25 | + | |
| 26 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Envelope ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 27 | +void tEnvelope_init(tEnvelope* const envlp, float attack, float decay, int loop) | |
| 28 | +{ | |
| 29 | + _tEnvelope* env = *envlp = (_tEnvelope*) leaf_alloc(sizeof(_tEnvelope)); | |
| 30 | + | |
| 31 | + env->exp_buff = __leaf_table_exp_decay; | |
| 32 | + env->inc_buff = __leaf_table_attack_decay_inc; | |
| 33 | + env->buff_size = sizeof(__leaf_table_exp_decay); | |
| 34 | + | |
| 35 | + env->loop = loop; | |
| 36 | + | |
| 37 | + if (attack > 8192.0f) | |
| 38 | + attack = 8192.0f; | |
| 39 | + if (attack < 0.0f) | |
| 40 | + attack = 0.0f; | |
| 41 | + | |
| 42 | + if (decay > 8192.0f) | |
| 43 | + decay = 8192.0f; | |
| 44 | + if (decay < 0.0f) | |
| 45 | + decay = 0.0f; | |
| 46 | + | |
| 47 | + int16_t attackIndex = ((int16_t)(attack * 8.0f))-1; | |
| 48 | + int16_t decayIndex = ((int16_t)(decay * 8.0f))-1; | |
| 49 | + int16_t rampIndex = ((int16_t)(2.0f * 8.0f))-1; | |
| 50 | + | |
| 51 | + if (attackIndex < 0) | |
| 52 | + attackIndex = 0; | |
| 53 | + if (decayIndex < 0) | |
| 54 | + decayIndex = 0; | |
| 55 | + if (rampIndex < 0) | |
| 56 | + rampIndex = 0; | |
| 57 | + | |
| 58 | + env->inRamp = OFALSE; | |
| 59 | + env->inAttack = OFALSE; | |
| 60 | + env->inDecay = OFALSE; | |
| 61 | + | |
| 62 | + env->attackInc = env->inc_buff[attackIndex]; | |
| 63 | + env->decayInc = env->inc_buff[decayIndex]; | |
| 64 | + env->rampInc = env->inc_buff[rampIndex]; | |
| 65 | +} | |
| 66 | + | |
| 67 | +void tEnvelope_free(tEnvelope* const envlp) | |
| 68 | +{ | |
| 69 | + _tEnvelope* env = *envlp; | |
| 70 | + leaf_free((char*)env); | |
| 71 | +} | |
| 72 | + | |
| 73 | +void tEnvelope_initToPool (tEnvelope* const envlp, float attack, float decay, int loop, tMempool* const mp) | |
| 74 | +{ | |
| 75 | + _tMempool* m = *mp; | |
| 76 | + _tEnvelope* env = *envlp = (_tEnvelope*) mpool_alloc(sizeof(_tEnvelope), m); | |
| 77 | + | |
| 78 | + env->exp_buff = __leaf_table_exp_decay; | |
| 79 | + env->inc_buff = __leaf_table_attack_decay_inc; | |
| 80 | + env->buff_size = sizeof(__leaf_table_exp_decay); | |
| 81 | + | |
| 82 | + env->loop = loop; | |
| 83 | + | |
| 84 | + if (attack > 8192.0f) | |
| 85 | + attack = 8192.0f; | |
| 86 | + if (attack < 0.0f) | |
| 87 | + attack = 0.0f; | |
| 88 | + | |
| 89 | + if (decay > 8192.0f) | |
| 90 | + decay = 8192.0f; | |
| 91 | + if (decay < 0.0f) | |
| 92 | + decay = 0.0f; | |
| 93 | + | |
| 94 | + int16_t attackIndex = ((int16_t)(attack * 8.0f))-1; | |
| 95 | + int16_t decayIndex = ((int16_t)(decay * 8.0f))-1; | |
| 96 | + int16_t rampIndex = ((int16_t)(2.0f * 8.0f))-1; | |
| 97 | + | |
| 98 | + if (attackIndex < 0) | |
| 99 | + attackIndex = 0; | |
| 100 | + if (decayIndex < 0) | |
| 101 | + decayIndex = 0; | |
| 102 | + if (rampIndex < 0) | |
| 103 | + rampIndex = 0; | |
| 104 | + | |
| 105 | + env->inRamp = OFALSE; | |
| 106 | + env->inAttack = OFALSE; | |
| 107 | + env->inDecay = OFALSE; | |
| 108 | + | |
| 109 | + env->attackInc = env->inc_buff[attackIndex]; | |
| 110 | + env->decayInc = env->inc_buff[decayIndex]; | |
| 111 | + env->rampInc = env->inc_buff[rampIndex]; | |
| 112 | +} | |
| 113 | + | |
| 114 | +void tEnvelope_freeFromPool (tEnvelope* const envlp, tMempool* const mp) | |
| 115 | +{ | |
| 116 | + _tMempool* m = *mp; | |
| 117 | + _tEnvelope* env = *envlp; | |
| 118 | + mpool_free((char*)env, m); | |
| 119 | +} | |
| 120 | + | |
| 121 | +void tEnvelope_setAttack(tEnvelope* const envlp, float attack) | |
| 122 | +{ | |
| 123 | + _tEnvelope* env = *envlp; | |
| 124 | + | |
| 125 | + int32_t attackIndex; | |
| 126 | + | |
| 127 | + if (attack < 0.0f) { | |
| 128 | + attackIndex = 0.0f; | |
| 129 | + } else if (attack < 8192.0f) { | |
| 130 | + attackIndex = ((int32_t)(attack * 8.0f))-1; | |
| 131 | + } else { | |
| 132 | + attackIndex = ((int32_t)(8192.0f * 8.0f))-1; | |
| 133 | + } | |
| 134 | + | |
| 135 | + env->attackInc = env->inc_buff[attackIndex]; | |
| 136 | +} | |
| 137 | + | |
| 138 | +void tEnvelope_setDecay(tEnvelope* const envlp, float decay) | |
| 139 | +{ | |
| 140 | + _tEnvelope* env = *envlp; | |
| 141 | + | |
| 142 | + int32_t decayIndex; | |
| 143 | + | |
| 144 | + if (decay < 0.0f) { | |
| 145 | + decayIndex = 0; | |
| 146 | + } else if (decay < 8192.0f) { | |
| 147 | + decayIndex = ((int32_t)(decay * 8.0f)) - 1; | |
| 148 | + } else { | |
| 149 | + decayIndex = ((int32_t)(8192.0f * 8.0f)) - 1; | |
| 150 | + } | |
| 151 | + | |
| 152 | + env->decayInc = env->inc_buff[decayIndex]; | |
| 153 | +} | |
| 154 | + | |
| 155 | +void tEnvelope_loop(tEnvelope* const envlp, int loop) | |
| 156 | +{ | |
| 157 | + _tEnvelope* env = *envlp; | |
| 158 | + env->loop = loop; | |
| 159 | +} | |
| 160 | + | |
| 161 | + | |
| 162 | +void tEnvelope_on(tEnvelope* const envlp, float velocity) | |
| 163 | +{ | |
| 164 | + _tEnvelope* env = *envlp; | |
| 165 | + | |
| 166 | + if (env->inAttack || env->inDecay) // In case envelope retriggered while it is still happening. | |
| 167 | + { | |
| 168 | + env->rampPhase = 0; | |
| 169 | + env->inRamp = OTRUE; | |
| 170 | + env->rampPeak = env->next; | |
| 171 | + } | |
| 172 | + else // Normal start. | |
| 173 | + { | |
| 174 | + env->inAttack = OTRUE; | |
| 175 | + } | |
| 176 | + | |
| 177 | + | |
| 178 | + env->attackPhase = 0; | |
| 179 | + env->decayPhase = 0; | |
| 180 | + env->inDecay = OFALSE; | |
| 181 | + env->gain = velocity; | |
| 182 | +} | |
| 183 | + | |
| 184 | +float tEnvelope_tick(tEnvelope* const envlp) | |
| 185 | +{ | |
| 186 | + _tEnvelope* env = *envlp; | |
| 187 | + | |
| 188 | + if (env->inRamp) | |
| 189 | + { | |
| 190 | + if (env->rampPhase > UINT16_MAX) | |
| 191 | + { | |
| 192 | + env->inRamp = OFALSE; | |
| 193 | + env->inAttack = OTRUE; | |
| 194 | + env->next = 0.0f; | |
| 195 | + } | |
| 196 | + else | |
| 197 | + { | |
| 198 | + env->next = env->rampPeak * env->exp_buff[(uint32_t)env->rampPhase]; | |
| 199 | + } | |
| 200 | + | |
| 201 | + env->rampPhase += env->rampInc; | |
| 202 | + } | |
| 203 | + | |
| 204 | + if (env->inAttack) | |
| 205 | + { | |
| 206 | + | |
| 207 | + // If attack done, time to turn around. | |
| 208 | + if (env->attackPhase > UINT16_MAX) | |
| 209 | + { | |
| 210 | + env->inDecay = OTRUE; | |
| 211 | + env->inAttack = OFALSE; | |
| 212 | + env->next = env->gain * 1.0f; | |
| 213 | + } | |
| 214 | + else | |
| 215 | + { | |
| 216 | + // do interpolation ! | |
| 217 | + env->next = env->gain * env->exp_buff[UINT16_MAX - (uint32_t)env->attackPhase]; // inverted and backwards to get proper rising exponential shape/perception | |
| 218 | + } | |
| 219 | + | |
| 220 | + // Increment envelope attack. | |
| 221 | + env->attackPhase += env->attackInc; | |
| 222 | + | |
| 223 | + } | |
| 224 | + | |
| 225 | + if (env->inDecay) | |
| 226 | + { | |
| 227 | + | |
| 228 | + // If decay done, finish. | |
| 229 | + if (env->decayPhase >= UINT16_MAX) | |
| 230 | + { | |
| 231 | + env->inDecay = OFALSE; | |
| 232 | + | |
| 233 | + if (env->loop) | |
| 234 | + { | |
| 235 | + env->attackPhase = 0; | |
| 236 | + env->decayPhase = 0; | |
| 237 | + env->inAttack = OTRUE; | |
| 238 | + } | |
| 239 | + else | |
| 240 | + { | |
| 241 | + env->next = 0.0f; | |
| 242 | + } | |
| 243 | + | |
| 244 | + } else { | |
| 245 | + | |
| 246 | + env->next = env->gain * (env->exp_buff[(uint32_t)env->decayPhase]); // do interpolation ! | |
| 247 | + } | |
| 248 | + | |
| 249 | + // Increment envelope decay; | |
| 250 | + env->decayPhase += env->decayInc; | |
| 251 | + } | |
| 252 | + | |
| 253 | + return env->next; | |
| 254 | +} | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | +/* ADSR */ | |
| 259 | +void tADSR_init(tADSR* const adsrenv, float attack, float decay, float sustain, float release) | |
| 260 | +{ | |
| 261 | + tADSR_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool); | |
| 262 | +} | |
| 263 | + | |
| 264 | +void tADSR_free(tADSR* const adsrenv) | |
| 265 | +{ | |
| 266 | + tADSR_freeFromPool(adsrenv, &leaf.mempool); | |
| 267 | +} | |
| 268 | + | |
| 269 | +void tADSR_initToPool (tADSR* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp) | |
| 270 | +{ | |
| 271 | + _tMempool* m = *mp; | |
| 272 | + _tADSR* adsr = *adsrenv = (_tADSR*) mpool_alloc(sizeof(_tADSR), m); | |
| 273 | + | |
| 274 | + adsr->exp_buff = __leaf_table_exp_decay; | |
| 275 | + adsr->inc_buff = __leaf_table_attack_decay_inc; | |
| 276 | + adsr->buff_size = sizeof(__leaf_table_exp_decay); | |
| 277 | + | |
| 278 | + if (attack > 8192.0f) | |
| 279 | + attack = 8192.0f; | |
| 280 | + if (attack < 0.0f) | |
| 281 | + attack = 0.0f; | |
| 282 | + | |
| 283 | + if (decay > 8192.0f) | |
| 284 | + decay = 8192.0f; | |
| 285 | + if (decay < 0.0f) | |
| 286 | + decay = 0.0f; | |
| 287 | + | |
| 288 | + if (sustain > 1.0f) | |
| 289 | + sustain = 1.0f; | |
| 290 | + if (sustain < 0.0f) | |
| 291 | + sustain = 0.0f; | |
| 292 | + | |
| 293 | + if (release > 8192.0f) | |
| 294 | + release = 8192.0f; | |
| 295 | + if (release < 0.0f) | |
| 296 | + release = 0.0f; | |
| 297 | + | |
| 298 | + int16_t attackIndex = ((int16_t)(attack * 8.0f))-1; | |
| 299 | + int16_t decayIndex = ((int16_t)(decay * 8.0f))-1; | |
| 300 | + int16_t releaseIndex = ((int16_t)(release * 8.0f))-1; | |
| 301 | + int16_t rampIndex = ((int16_t)(2.0f * 8.0f))-1; | |
| 302 | + | |
| 303 | + if (attackIndex < 0) | |
| 304 | + attackIndex = 0; | |
| 305 | + if (decayIndex < 0) | |
| 306 | + decayIndex = 0; | |
| 307 | + if (releaseIndex < 0) | |
| 308 | + releaseIndex = 0; | |
| 309 | + if (rampIndex < 0) | |
| 310 | + rampIndex = 0; | |
| 311 | + | |
| 312 | + adsr->next = 0.0f; | |
| 313 | + | |
| 314 | + adsr->inRamp = OFALSE; | |
| 315 | + adsr->inAttack = OFALSE; | |
| 316 | + adsr->inDecay = OFALSE; | |
| 317 | + adsr->inSustain = OFALSE; | |
| 318 | + adsr->inRelease = OFALSE; | |
| 319 | + | |
| 320 | + adsr->sustain = sustain; | |
| 321 | + | |
| 322 | + adsr->attackInc = adsr->inc_buff[attackIndex]; | |
| 323 | + adsr->decayInc = adsr->inc_buff[decayIndex]; | |
| 324 | + adsr->releaseInc = adsr->inc_buff[releaseIndex]; | |
| 325 | + adsr->rampInc = adsr->inc_buff[rampIndex]; | |
| 326 | + | |
| 327 | + adsr->leakFactor = 1.0f; | |
| 328 | +} | |
| 329 | + | |
| 330 | +void tADSR_freeFromPool (tADSR* const adsrenv, tMempool* const mp) | |
| 331 | +{ | |
| 332 | + _tMempool* m = *mp; | |
| 333 | + _tADSR* adsr = *adsrenv; | |
| 334 | + mpool_free((char*)adsr, m); | |
| 335 | +} | |
| 336 | + | |
| 337 | +void tADSR_setAttack(tADSR* const adsrenv, float attack) | |
| 338 | +{ | |
| 339 | + _tADSR* adsr = *adsrenv; | |
| 340 | + | |
| 341 | + int32_t attackIndex; | |
| 342 | + | |
| 343 | + if (attack < 0.0f) { | |
| 344 | + attackIndex = 0.0f; | |
| 345 | + } else if (attack < 8192.0f) { | |
| 346 | + attackIndex = ((int32_t)(attack * 8.0f))-1; | |
| 347 | + } else { | |
| 348 | + attackIndex = ((int32_t)(8192.0f * 8.0f))-1; | |
| 349 | + } | |
| 350 | + | |
| 351 | + adsr->attackInc = adsr->inc_buff[attackIndex]; | |
| 352 | +} | |
| 353 | + | |
| 354 | +void tADSR_setDecay(tADSR* const adsrenv, float decay) | |
| 355 | +{ | |
| 356 | + _tADSR* adsr = *adsrenv; | |
| 357 | + | |
| 358 | + int32_t decayIndex; | |
| 359 | + | |
| 360 | + if (decay < 0.0f) { | |
| 361 | + decayIndex = 0.0f; | |
| 362 | + } else if (decay < 8192.0f) { | |
| 363 | + decayIndex = ((int32_t)(decay * 8.0f)) - 1; | |
| 364 | + } else { | |
| 365 | + decayIndex = ((int32_t)(8192.0f * 8.0f)) - 1; | |
| 366 | + } | |
| 367 | + | |
| 368 | + adsr->decayInc = adsr->inc_buff[decayIndex]; | |
| 369 | +} | |
| 370 | + | |
| 371 | +void tADSR_setSustain(tADSR* const adsrenv, float sustain) | |
| 372 | +{ | |
| 373 | + _tADSR* adsr = *adsrenv; | |
| 374 | + | |
| 375 | + if (sustain > 1.0f) adsr->sustain = 1.0f; | |
| 376 | + else if (sustain < 0.0f) adsr->sustain = 0.0f; | |
| 377 | + else adsr->sustain = sustain; | |
| 378 | +} | |
| 379 | + | |
| 380 | +void tADSR_setRelease(tADSR* const adsrenv, float release) | |
| 381 | +{ | |
| 382 | + _tADSR* adsr = *adsrenv; | |
| 383 | + | |
| 384 | + int32_t releaseIndex; | |
| 385 | + | |
| 386 | + if (release < 0.0f) { | |
| 387 | + releaseIndex = 0.0f; | |
| 388 | + } else if (release < 8192.0f) { | |
| 389 | + releaseIndex = ((int32_t)(release * 8.0f)) - 1; | |
| 390 | + } else { | |
| 391 | + releaseIndex = ((int32_t)(8192.0f * 8.0f)) - 1; | |
| 392 | + } | |
| 393 | + | |
| 394 | + adsr->releaseInc = adsr->inc_buff[releaseIndex]; | |
| 395 | +} | |
| 396 | + | |
| 397 | +// 0.999999 is slow leak, 0.9 is fast leak | |
| 398 | +void tADSR_setLeakFactor(tADSR* const adsrenv, float leakFactor) | |
| 399 | +{ | |
| 400 | + _tADSR* adsr = *adsrenv; | |
| 401 | + | |
| 402 | + | |
| 403 | + adsr->leakFactor = leakFactor; | |
| 404 | +} | |
| 405 | + | |
| 406 | +void tADSR_on(tADSR* const adsrenv, float velocity) | |
| 407 | +{ | |
| 408 | + _tADSR* adsr = *adsrenv; | |
| 409 | + | |
| 410 | + if ((adsr->inAttack || adsr->inDecay) || (adsr->inSustain || adsr->inRelease)) // In case ADSR retriggered while it is still happening. | |
| 411 | + { | |
| 412 | + adsr->rampPhase = 0; | |
| 413 | + adsr->inRamp = OTRUE; | |
| 414 | + adsr->rampPeak = adsr->next; | |
| 415 | + } | |
| 416 | + else // Normal start. | |
| 417 | + { | |
| 418 | + adsr->inAttack = OTRUE; | |
| 419 | + } | |
| 420 | + | |
| 421 | + adsr->attackPhase = 0; | |
| 422 | + adsr->decayPhase = 0; | |
| 423 | + adsr->releasePhase = 0; | |
| 424 | + adsr->inDecay = OFALSE; | |
| 425 | + adsr->inSustain = OFALSE; | |
| 426 | + adsr->inRelease = OFALSE; | |
| 427 | + adsr->gain = velocity; | |
| 428 | +} | |
| 429 | + | |
| 430 | +void tADSR_off(tADSR* const adsrenv) | |
| 431 | +{ | |
| 432 | + _tADSR* adsr = *adsrenv; | |
| 433 | + | |
| 434 | + if (adsr->inRelease) return; | |
| 435 | + | |
| 436 | + adsr->inAttack = OFALSE; | |
| 437 | + adsr->inDecay = OFALSE; | |
| 438 | + adsr->inSustain = OFALSE; | |
| 439 | + adsr->inRelease = OTRUE; | |
| 440 | + | |
| 441 | + adsr->releasePeak = adsr->next; | |
| 442 | +} | |
| 443 | + | |
| 444 | +float tADSR_tick(tADSR* const adsrenv) | |
| 445 | +{ | |
| 446 | + _tADSR* adsr = *adsrenv; | |
| 447 | + | |
| 448 | + | |
| 449 | + if (adsr->inRamp) | |
| 450 | + { | |
| 451 | + if (adsr->rampPhase > UINT16_MAX) | |
| 452 | + { | |
| 453 | + adsr->inRamp = OFALSE; | |
| 454 | + adsr->inAttack = OTRUE; | |
| 455 | + adsr->next = 0.0f; | |
| 456 | + } | |
| 457 | + else | |
| 458 | + { | |
| 459 | + adsr->next = adsr->rampPeak * adsr->exp_buff[(uint32_t)adsr->rampPhase]; | |
| 460 | + } | |
| 461 | + | |
| 462 | + adsr->rampPhase += adsr->rampInc; | |
| 463 | + } | |
| 464 | + | |
| 465 | + if (adsr->inAttack) | |
| 466 | + { | |
| 467 | + | |
| 468 | + // If attack done, time to turn around. | |
| 469 | + if (adsr->attackPhase > UINT16_MAX) | |
| 470 | + { | |
| 471 | + adsr->inDecay = OTRUE; | |
| 472 | + adsr->inAttack = OFALSE; | |
| 473 | + adsr->next = adsr->gain * 1.0f; | |
| 474 | + } | |
| 475 | + else | |
| 476 | + { | |
| 477 | + // do interpolation ! | |
| 478 | + adsr->next = adsr->gain * adsr->exp_buff[UINT16_MAX - (uint32_t)adsr->attackPhase]; // inverted and backwards to get proper rising exponential shape/perception | |
| 479 | + } | |
| 480 | + | |
| 481 | + // Increment ADSR attack. | |
| 482 | + adsr->attackPhase += adsr->attackInc; | |
| 483 | + | |
| 484 | + } | |
| 485 | + | |
| 486 | + if (adsr->inDecay) | |
| 487 | + { | |
| 488 | + | |
| 489 | + // If decay done, sustain. | |
| 490 | + if (adsr->decayPhase >= UINT16_MAX) | |
| 491 | + { | |
| 492 | + adsr->inDecay = OFALSE; | |
| 493 | + adsr->inSustain = OTRUE; | |
| 494 | + adsr->next = adsr->gain * adsr->sustain; | |
| 495 | + } | |
| 496 | + | |
| 497 | + else | |
| 498 | + { | |
| 499 | + adsr->next = (adsr->gain * (adsr->sustain + ((adsr->exp_buff[(uint32_t)adsr->decayPhase]) * (1.0f - adsr->sustain)))) * adsr->leakFactor; // do interpolation ! | |
| 500 | + } | |
| 501 | + | |
| 502 | + // Increment ADSR decay. | |
| 503 | + adsr->decayPhase += adsr->decayInc; | |
| 504 | + } | |
| 505 | + | |
| 506 | + if (adsr->inSustain) | |
| 507 | + { | |
| 508 | + adsr->next = adsr->next * adsr->leakFactor; | |
| 509 | + } | |
| 510 | + | |
| 511 | + if (adsr->inRelease) | |
| 512 | + { | |
| 513 | + // If release done, finish. | |
| 514 | + if (adsr->releasePhase >= UINT16_MAX) | |
| 515 | + { | |
| 516 | + adsr->inRelease = OFALSE; | |
| 517 | + adsr->next = 0.0f; | |
| 518 | + } | |
| 519 | + else { | |
| 520 | + | |
| 521 | + adsr->next = adsr->releasePeak * (adsr->exp_buff[(uint32_t)adsr->releasePhase]); // do interpolation ! | |
| 522 | + } | |
| 523 | + | |
| 524 | + // Increment envelope release; | |
| 525 | + adsr->releasePhase += adsr->releaseInc; | |
| 526 | + } | |
| 527 | + | |
| 528 | + | |
| 529 | + return adsr->next; | |
| 530 | +} | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | +/* ADSR 2*/ | |
| 536 | +//This one is adapted from the VCV Rack code | |
| 537 | +//-JS | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | +const float ADSR2_MIN_TIME = 1e-3f; | |
| 542 | +const float ADSR2_INV_MIN_TIME = 1000.0f; | |
| 543 | +const float ADSR2_MAX_TIME = 10.f; | |
| 544 | +const float ADSR2_LAMBDA_BASE = 10000.0f; | |
| 545 | + | |
| 546 | + | |
| 547 | +void tADSR2_init(tADSR2* const adsrenv, float attack, float decay, float sustain, float release) | |
| 548 | +{ | |
| 549 | + tADSR2_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool); | |
| 550 | +} | |
| 551 | + | |
| 552 | +void tADSR2_free(tADSR2* const adsrenv) | |
| 553 | +{ | |
| 554 | + tADSR2_freeFromPool(adsrenv, &leaf.mempool); | |
| 555 | +} | |
| 556 | + | |
| 557 | +void tADSR2_initToPool (tADSR2* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp) | |
| 558 | +{ | |
| 559 | + _tMempool* m = *mp; | |
| 560 | + _tADSR2* adsr = *adsrenv = (_tADSR2*) mpool_alloc(sizeof(_tADSR2), m); | |
| 561 | + adsr->sampleRateInMs = leaf.sampleRate * 0.001f; | |
| 562 | + adsr->attack = LEAF_clip(0.0f, attack * 0.001f, 1.0f); | |
| 563 | + adsr->attackLambda = powf(ADSR2_LAMBDA_BASE, -adsr->attack) * ADSR2_INV_MIN_TIME; | |
| 564 | + | |
| 565 | + | |
| 566 | + adsr->decay = LEAF_clip(0.0f, decay * 0.001f, 1.0f); | |
| 567 | + adsr->decayLambda = powf(ADSR2_LAMBDA_BASE, -adsr->decay) * ADSR2_INV_MIN_TIME; | |
| 568 | + | |
| 569 | + | |
| 570 | + adsr->sustain= sustain; | |
| 571 | + | |
| 572 | + | |
| 573 | + adsr->release = LEAF_clip(0.0f, release * 0.001f, 1.0f); | |
| 574 | + adsr->releaseLambda = powf(ADSR2_LAMBDA_BASE, -adsr->release) * ADSR2_INV_MIN_TIME; | |
| 575 | + | |
| 576 | + adsr->attacking = 0; | |
| 577 | + adsr->gate = 0; | |
| 578 | + adsr->gain = 1.0f; | |
| 579 | + adsr->targetGainSquared = 1.0f; | |
| 580 | + adsr->factor = 0.01f; | |
| 581 | + adsr->oneMinusFactor = 0.99f; | |
| 582 | + adsr->env = 0.0f; | |
| 583 | + adsr->leakFactor = 1.0f; | |
| 584 | +} | |
| 585 | + | |
| 586 | +void tADSR2_freeFromPool (tADSR2* const adsrenv, tMempool* const mp) | |
| 587 | +{ | |
| 588 | + _tMempool* m = *mp; | |
| 589 | + _tADSR2* adsr = *adsrenv; | |
| 590 | + mpool_free((char*)adsr, m); | |
| 591 | +} | |
| 592 | + | |
| 593 | +void tADSR2_setAttack(tADSR2* const adsrenv, float attack) | |
| 594 | +{ | |
| 595 | + _tADSR2* adsr = *adsrenv; | |
| 596 | + adsr->attack = LEAF_clip(0.0f, attack * 0.001f, 1.0f); | |
| 597 | + adsr->attackLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->attack) * ADSR2_INV_MIN_TIME; | |
| 598 | + | |
| 599 | +} | |
| 600 | + | |
| 601 | +void tADSR2_setDecay(tADSR2* const adsrenv, float decay) | |
| 602 | +{ | |
| 603 | + _tADSR2* adsr = *adsrenv; | |
| 604 | + adsr->decay = LEAF_clip(0.0f, decay * 0.001f, 1.0f); | |
| 605 | + adsr->decayLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->decay) * ADSR2_INV_MIN_TIME; | |
| 606 | + | |
| 607 | +} | |
| 608 | + | |
| 609 | +void tADSR2_setSustain(tADSR2* const adsrenv, float sustain) | |
| 610 | +{ | |
| 611 | + _tADSR2* adsr = *adsrenv; | |
| 612 | + adsr->sustain = sustain; | |
| 613 | + | |
| 614 | + if (adsr->gate) | |
| 615 | + { | |
| 616 | + if (adsr->attacking == 0) | |
| 617 | + { | |
| 618 | + adsr->envTarget = sustain; | |
| 619 | + } | |
| 620 | + } | |
| 621 | +} | |
| 622 | + | |
| 623 | +void tADSR2_setRelease(tADSR2* const adsrenv, float release) | |
| 624 | +{ | |
| 625 | + _tADSR2* adsr = *adsrenv; | |
| 626 | + adsr->release = LEAF_clip(0.0f, release * 0.001f, 1.0f); | |
| 627 | + adsr->releaseLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->release) * ADSR2_INV_MIN_TIME; | |
| 628 | +} | |
| 629 | + | |
| 630 | +// 0.999999 is slow leak, 0.9 is fast leak | |
| 631 | +void tADSR2_setLeakFactor(tADSR2* const adsrenv, float leakFactor) | |
| 632 | +{ | |
| 633 | + _tADSR2* adsr = *adsrenv; | |
| 634 | + | |
| 635 | + adsr->leakFactor = leakFactor; | |
| 636 | +} | |
| 637 | + | |
| 638 | +void tADSR2_on(tADSR2* const adsrenv, float velocity) | |
| 639 | +{ | |
| 640 | + _tADSR2* adsr = *adsrenv; | |
| 641 | + adsr->targetGainSquared = velocity * velocity; | |
| 642 | + adsr->envTarget = 1.2f; | |
| 643 | + adsr->leakGain = 1.0f; | |
| 644 | + adsr->gate = 1; | |
| 645 | + adsr->attacking = 1; | |
| 646 | +} | |
| 647 | + | |
| 648 | +void tADSR2_off(tADSR2* const adsrenv) | |
| 649 | +{ | |
| 650 | + _tADSR2* adsr = *adsrenv; | |
| 651 | + adsr->gate = 0; | |
| 652 | + adsr->envTarget = 0.0f; | |
| 653 | +} | |
| 654 | + | |
| 655 | +float tADSR2_tick(tADSR2* const adsrenv) | |
| 656 | +{ | |
| 657 | + _tADSR2* adsr = *adsrenv; | |
| 658 | + float lambda; | |
| 659 | + | |
| 660 | + if (adsr->gate) | |
| 661 | + { | |
| 662 | + if (adsr->attacking) | |
| 663 | + { | |
| 664 | + lambda = adsr->attackLambda; | |
| 665 | + } | |
| 666 | + else | |
| 667 | + { | |
| 668 | + lambda = adsr->decayLambda; | |
| 669 | + } | |
| 670 | + } | |
| 671 | + else | |
| 672 | + { | |
| 673 | + lambda = adsr->releaseLambda; | |
| 674 | + } | |
| 675 | + | |
| 676 | + | |
| 677 | + // Adjust env | |
| 678 | + adsr->env += (adsr->envTarget - adsr->env) * lambda * leaf.invSampleRate; | |
| 679 | + | |
| 680 | + // Turn off attacking state if envelope is HIGH | |
| 681 | + if (adsr->env >= 1.0f) | |
| 682 | + { | |
| 683 | + adsr->attacking = 0; | |
| 684 | + adsr->envTarget = adsr->sustain; | |
| 685 | + } | |
| 686 | + | |
| 687 | + //smooth the gain value -- this is not ideal, a retrigger while the envelope is still going with a new gain will cause a jump, although it will be smoothed quickly. Maybe doing the math so the range is computed based on the gain rather than 0.->1. is preferable? But that's harder to get the exponential curve right without a lookup. | |
| 688 | + adsr->gain = ((adsr->factor*adsr->targetGainSquared)+(adsr->oneMinusFactor*adsr->gain)); | |
| 689 | + adsr->leakGain *= adsr->leakFactor; | |
| 690 | + return adsr->env * adsr->gain * adsr->leakGain; | |
| 691 | +} | |
| 692 | + | |
| 693 | +/* ADSR 3*/ | |
| 694 | +//This one doesn't use any lookup table - by Nigel Redmon from his blog. Thanks, Nigel! | |
| 695 | +//-JS | |
| 696 | + | |
| 697 | +float calcADSR3Coef(double rate, double targetRatio) | |
| 698 | +{ | |
| 699 | + return (rate <= 0.0f) ? 0.0f : exp(-log((1.0 + targetRatio) / targetRatio) / rate); | |
| 700 | +} | |
| 701 | + | |
| 702 | + | |
| 703 | +void tADSR3_init(tADSR3* const adsrenv, float attack, float decay, float sustain, float release) | |
| 704 | +{ | |
| 705 | + tADSR3_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool); | |
| 706 | +} | |
| 707 | + | |
| 708 | +void tADSR3_free(tADSR3* const adsrenv) | |
| 709 | +{ | |
| 710 | + tADSR3_freeFromPool(adsrenv, &leaf.mempool); | |
| 711 | +} | |
| 712 | + | |
| 713 | +void tADSR3_initToPool (tADSR3* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp) | |
| 714 | +{ | |
| 715 | + _tMempool* m = *mp; | |
| 716 | + _tADSR3* adsr = *adsrenv = (_tADSR3*) mpool_alloc(sizeof(_tADSR3), m); | |
| 717 | + adsr->sampleRateInMs = leaf.sampleRate * 0.001f; | |
| 718 | + adsr->targetRatioA = 0.3f; | |
| 719 | + adsr->targetRatioDR = 0.0001f; | |
| 720 | + adsr->attackRate = attack * adsr->sampleRateInMs; | |
| 721 | + adsr->attackCoef = calcADSR3Coef(attack * adsr->sampleRateInMs, adsr->targetRatioA); | |
| 722 | + adsr->attackBase = (1.0f + adsr->targetRatioA) * (1.0f - adsr->attackCoef); | |
| 723 | + | |
| 724 | + adsr->decayRate = decay * adsr->sampleRateInMs; | |
| 725 | + adsr->decayCoef = calcADSR3Coef(decay * adsr->sampleRateInMs,adsr-> targetRatioDR); | |
| 726 | + adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef); | |
| 727 | + | |
| 728 | + adsr->sustainLevel = sustain; | |
| 729 | + adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef); | |
| 730 | + | |
| 731 | + adsr->releaseRate = release * adsr->sampleRateInMs; | |
| 732 | + adsr->releaseCoef = calcADSR3Coef(release * adsr->sampleRateInMs, adsr->targetRatioDR); | |
| 733 | + adsr->releaseBase = -adsr->targetRatioDR * (1.0f - adsr->releaseCoef); | |
| 734 | + | |
| 735 | + adsr->state = env_idle; | |
| 736 | + adsr->gain = 1.0f; | |
| 737 | + adsr->targetGainSquared = 1.0f; | |
| 738 | + adsr->factor = 0.01f; | |
| 739 | + adsr->oneMinusFactor = 0.99f; | |
| 740 | + adsr->output = 0.0f; | |
| 741 | + adsr->leakFactor = 1.0f; | |
| 742 | +} | |
| 743 | + | |
| 744 | +void tADSR3_freeFromPool (tADSR3* const adsrenv, tMempool* const mp) | |
| 745 | +{ | |
| 746 | + _tMempool* m = *mp; | |
| 747 | + _tADSR3* adsr = *adsrenv; | |
| 748 | + mpool_free((char*)adsr, m); | |
| 749 | +} | |
| 750 | + | |
| 751 | +void tADSR3_setAttack(tADSR3* const adsrenv, float attack) | |
| 752 | +{ | |
| 753 | + _tADSR3* adsr = *adsrenv; | |
| 754 | + | |
| 755 | + adsr->attackRate = attack * adsr->sampleRateInMs; | |
| 756 | + adsr->attackCoef = calcADSR3Coef(adsr->attackRate, adsr->targetRatioA); | |
| 757 | + adsr->attackBase = (1.0f + adsr->targetRatioA) * (1.0f - adsr->attackCoef); | |
| 758 | +} | |
| 759 | + | |
| 760 | +void tADSR3_setDecay(tADSR3* const adsrenv, float decay) | |
| 761 | +{ | |
| 762 | + _tADSR3* adsr = *adsrenv; | |
| 763 | + | |
| 764 | + adsr->decayRate = decay * adsr->sampleRateInMs; | |
| 765 | + adsr->decayCoef = calcADSR3Coef(adsr->decayRate,adsr-> targetRatioDR); | |
| 766 | + adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef); | |
| 767 | +} | |
| 768 | + | |
| 769 | +void tADSR3_setSustain(tADSR3* const adsrenv, float sustain) | |
| 770 | +{ | |
| 771 | + _tADSR3* adsr = *adsrenv; | |
| 772 | + | |
| 773 | + adsr->sustainLevel = sustain; | |
| 774 | + adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef); | |
| 775 | +} | |
| 776 | + | |
| 777 | +void tADSR3_setRelease(tADSR3* const adsrenv, float release) | |
| 778 | +{ | |
| 779 | + _tADSR3* adsr = *adsrenv; | |
| 780 | + | |
| 781 | + adsr->releaseRate = release * adsr->sampleRateInMs; | |
| 782 | + adsr->releaseCoef = calcADSR3Coef(adsr->releaseRate, adsr->targetRatioDR); | |
| 783 | + adsr->releaseBase = -adsr->targetRatioDR * (1.0f - adsr->releaseCoef); | |
| 784 | +} | |
| 785 | + | |
| 786 | +// 0.999999 is slow leak, 0.9 is fast leak | |
| 787 | +void tADSR3_setLeakFactor(tADSR3* const adsrenv, float leakFactor) | |
| 788 | +{ | |
| 789 | + _tADSR3* adsr = *adsrenv; | |
| 790 | + | |
| 791 | + adsr->leakFactor = leakFactor; | |
| 792 | +} | |
| 793 | + | |
| 794 | +void tADSR3_on(tADSR3* const adsrenv, float velocity) | |
| 795 | +{ | |
| 796 | + _tADSR3* adsr = *adsrenv; | |
| 797 | + adsr->state = env_attack; | |
| 798 | + adsr->targetGainSquared = velocity * velocity; | |
| 799 | +} | |
| 800 | + | |
| 801 | +void tADSR3_off(tADSR3* const adsrenv) | |
| 802 | +{ | |
| 803 | + _tADSR3* adsr = *adsrenv; | |
| 804 | + | |
| 805 | + if (adsr->state != env_idle) | |
| 806 | + { | |
| 807 | + adsr->state = env_release; | |
| 808 | + } | |
| 809 | +} | |
| 810 | + | |
| 811 | +float tADSR3_tick(tADSR3* const adsrenv) | |
| 812 | +{ | |
| 813 | + _tADSR3* adsr = *adsrenv; | |
| 814 | + | |
| 815 | + | |
| 816 | + switch (adsr->state) { | |
| 817 | + case env_idle: | |
| 818 | + break; | |
| 819 | + case env_attack: | |
| 820 | + adsr->output = adsr->attackBase + adsr->output * adsr->attackCoef; | |
| 821 | + if (adsr->output >= 1.0f) { | |
| 822 | + adsr->output = 1.0f; | |
| 823 | + adsr->state = env_decay; | |
| 824 | + } | |
| 825 | + break; | |
| 826 | + case env_decay: | |
| 827 | + adsr->output = adsr->decayBase + adsr->output * adsr->decayCoef * adsr->leakFactor; | |
| 828 | + if (adsr->output <= adsr->sustainLevel) { | |
| 829 | + adsr->output = adsr->sustainLevel; | |
| 830 | + adsr->state = env_sustain; | |
| 831 | + } | |
| 832 | + break; | |
| 833 | + case env_sustain: | |
| 834 | + adsr->output = adsr->output * adsr->leakFactor; | |
| 835 | + break; | |
| 836 | + case env_release: | |
| 837 | + adsr->output = adsr->releaseBase + adsr->output * adsr->releaseCoef; | |
| 838 | + if (adsr->output <= 0.0f) { | |
| 839 | + adsr->output = 0.0f; | |
| 840 | + adsr->state = env_idle; | |
| 841 | + } | |
| 842 | + } | |
| 843 | + //smooth the gain value -- this is not ideal, a retrigger while the envelope is still going with a new gain will cause a jump, although it will be smoothed quickly. Maybe doing the math so the range is computed based on the gain rather than 0.->1. is preferable? But that's harder to get the exponential curve right without a lookup. | |
| 844 | + adsr->gain = (adsr->factor*adsr->targetGainSquared)+(adsr->oneMinusFactor*adsr->gain); | |
| 845 | + return adsr->output * adsr->gain; | |
| 846 | +} | |
| 847 | + | |
| 848 | + | |
| 849 | +/* ADSR 4 */ // new version of our original table-based ADSR but with the table passed in by the user | |
| 850 | +// use this if the size of the big ADSR tables is too much. | |
| 851 | +void tADSR4_init (tADSR4* const adsrenv, float attack, float decay, float sustain, float release, float* expBuffer, int bufferSize) | |
| 852 | +{ | |
| 853 | + tADSR4_initToPool (adsrenv, attack, decay, sustain, release, expBuffer, bufferSize, &leaf.mempool); | |
| 854 | +} | |
| 855 | + | |
| 856 | +void tADSR4_free(tADSR4* const adsrenv) | |
| 857 | +{ | |
| 858 | + tADSR4_freeFromPool(adsrenv, &leaf.mempool); | |
| 859 | +} | |
| 860 | + | |
| 861 | +//initialize with an exponential function that decays -- i.e. a call to LEAF_generate_exp(expBuffer, 0.001f, 0.0f, 1.0f, -0.0008f, EXP_BUFFER_SIZE); | |
| 862 | +//times are in ms | |
| 863 | +void tADSR4_initToPool (tADSR4* const adsrenv, float attack, float decay, float sustain, float release, float* expBuffer, int bufferSize, tMempool* const mp) | |
| 864 | +{ | |
| 865 | + _tMempool* m = *mp; | |
| 866 | + _tADSR4* adsr = *adsrenv = (_tADSR4*) mpool_alloc(sizeof(_tADSR4), m); | |
| 867 | + | |
| 868 | + adsr->exp_buff = expBuffer; | |
| 869 | + adsr->buff_size = bufferSize; | |
| 870 | + adsr->buff_sizeMinusOne = bufferSize - 1; | |
| 871 | + | |
| 872 | + adsr->bufferSizeDividedBySampleRateInMs = bufferSize / (leaf.sampleRate * 0.001f); | |
| 873 | + | |
| 874 | + if (attack < 0.0f) | |
| 875 | + attack = 0.0f; | |
| 876 | + | |
| 877 | + if (decay < 0.0f) | |
| 878 | + decay = 0.0f; | |
| 879 | + | |
| 880 | + if (sustain > 1.0f) | |
| 881 | + sustain = 1.0f; | |
| 882 | + if (sustain < 0.0f) | |
| 883 | + sustain = 0.0f; | |
| 884 | + | |
| 885 | + if (release < 0.0f) | |
| 886 | + release = 0.0f; | |
| 887 | + | |
| 888 | + adsr->next = 0.0f; | |
| 889 | + | |
| 890 | + adsr->whichStage = env_idle; | |
| 891 | + | |
| 892 | + adsr->sustain = sustain; | |
| 893 | + | |
| 894 | + adsr->attackInc = adsr->bufferSizeDividedBySampleRateInMs / attack; | |
| 895 | + adsr->decayInc = adsr->bufferSizeDividedBySampleRateInMs / decay; | |
| 896 | + adsr->releaseInc = adsr->bufferSizeDividedBySampleRateInMs / release; | |
| 897 | + adsr->rampInc = adsr->bufferSizeDividedBySampleRateInMs / 8.0f; | |
| 898 | + | |
| 899 | + adsr->leakFactor = 1.0f; | |
| 900 | +} | |
| 901 | + | |
| 902 | +void tADSR4_freeFromPool (tADSR4* const adsrenv, tMempool* const mp) | |
| 903 | +{ | |
| 904 | + _tMempool* m = *mp; | |
| 905 | + _tADSR4* adsr = *adsrenv; | |
| 906 | + mpool_free((char*)adsr, m); | |
| 907 | +} | |
| 908 | + | |
| 909 | +void tADSR4_setAttack(tADSR4* const adsrenv, float attack) | |
| 910 | +{ | |
| 911 | + _tADSR4* adsr = *adsrenv; | |
| 912 | + | |
| 913 | + if (attack < 0.0f) | |
| 914 | + { | |
| 915 | + attack = 0.0f; | |
| 916 | + } | |
| 917 | + | |
| 918 | + adsr->attackInc = adsr->bufferSizeDividedBySampleRateInMs / attack; | |
| 919 | +} | |
| 920 | + | |
| 921 | +void tADSR4_setDecay(tADSR4* const adsrenv, float decay) | |
| 922 | +{ | |
| 923 | + _tADSR4* adsr = *adsrenv; | |
| 924 | + | |
| 925 | + if (decay < 0.0f) | |
| 926 | + { | |
| 927 | + decay = 0.0f; | |
| 928 | + } | |
| 929 | + adsr->decayInc = adsr->bufferSizeDividedBySampleRateInMs / decay; | |
| 930 | +} | |
| 931 | + | |
| 932 | +void tADSR4_setSustain(tADSR4* const adsrenv, float sustain) | |
| 933 | +{ | |
| 934 | + _tADSR4* adsr = *adsrenv; | |
| 935 | + | |
| 936 | + if (sustain > 1.0f) adsr->sustain = 1.0f; | |
| 937 | + else if (sustain < 0.0f) adsr->sustain = 0.0f; | |
| 938 | + else adsr->sustain = sustain; | |
| 939 | +} | |
| 940 | + | |
| 941 | +void tADSR4_setRelease(tADSR4* const adsrenv, float release) | |
| 942 | +{ | |
| 943 | + _tADSR4* adsr = *adsrenv; | |
| 944 | + | |
| 945 | + if (release < 0.0f) | |
| 946 | + { | |
| 947 | + release = 0.0f; | |
| 948 | + } | |
| 949 | + adsr->releaseInc = adsr->bufferSizeDividedBySampleRateInMs / release; | |
| 950 | +} | |
| 951 | + | |
| 952 | +// 0.999999 is slow leak, 0.9 is fast leak | |
| 953 | +void tADSR4_setLeakFactor(tADSR4* const adsrenv, float leakFactor) | |
| 954 | +{ | |
| 955 | + _tADSR4* adsr = *adsrenv; | |
| 956 | + | |
| 957 | + | |
| 958 | + adsr->leakFactor = leakFactor; | |
| 959 | +} | |
| 960 | + | |
| 961 | +void tADSR4_on(tADSR4* const adsrenv, float velocity) | |
| 962 | +{ | |
| 963 | + _tADSR4* adsr = *adsrenv; | |
| 964 | + | |
| 965 | + if (adsr->whichStage != env_idle) // In case ADSR retriggered while it is still happening. | |
| 966 | + { | |
| 967 | + adsr->rampPhase = 0; | |
| 968 | + adsr->whichStage = env_ramp; | |
| 969 | + adsr->rampPeak = adsr->next; | |
| 970 | + } | |
| 971 | + else // Normal start. | |
| 972 | + { | |
| 973 | + adsr->whichStage = env_attack; | |
| 974 | + } | |
| 975 | + | |
| 976 | + adsr->attackPhase = 0; | |
| 977 | + adsr->decayPhase = 0; | |
| 978 | + adsr->releasePhase = 0; | |
| 979 | + adsr->gain = velocity; | |
| 980 | +} | |
| 981 | + | |
| 982 | +void tADSR4_off(tADSR4* const adsrenv) | |
| 983 | +{ | |
| 984 | + _tADSR4* adsr = *adsrenv; | |
| 985 | + | |
| 986 | + if (adsr->whichStage == env_idle) | |
| 987 | + { | |
| 988 | + return; | |
| 989 | + } | |
| 990 | + else | |
| 991 | + { | |
| 992 | + adsr->whichStage = env_release; | |
| 993 | + adsr->releasePeak = adsr->next; | |
| 994 | + } | |
| 995 | +} | |
| 996 | + | |
| 997 | +float tADSR4_tick(tADSR4* const adsrenv) | |
| 998 | +{ | |
| 999 | + _tADSR4* adsr = *adsrenv; | |
| 1000 | + | |
| 1001 | + switch (adsr->whichStage) | |
| 1002 | + { | |
| 1003 | + case env_ramp: | |
| 1004 | + if (adsr->rampPhase > adsr->buff_sizeMinusOne) | |
| 1005 | + { | |
| 1006 | + adsr->whichStage = env_attack; | |
| 1007 | + adsr->next = 0.0f; | |
| 1008 | + } | |
| 1009 | + else | |
| 1010 | + { | |
| 1011 | + uint32_t intPart = (uint32_t)adsr->rampPhase; | |
| 1012 | + float floatPart = adsr->rampPhase - intPart; | |
| 1013 | + float secondValue; | |
| 1014 | + if (adsr->rampPhase + 1.0f > adsr->buff_sizeMinusOne) | |
| 1015 | + { | |
| 1016 | + secondValue = 0.0f; | |
| 1017 | + } | |
| 1018 | + else | |
| 1019 | + { | |
| 1020 | + secondValue = adsr->exp_buff[(uint32_t)((adsr->rampPhase)+1)]; | |
| 1021 | + } | |
| 1022 | + adsr->next = adsr->rampPeak * LEAF_interpolation_linear(adsr->exp_buff[intPart], secondValue, floatPart); | |
| 1023 | + } | |
| 1024 | + | |
| 1025 | + adsr->rampPhase += adsr->rampInc; | |
| 1026 | + break; | |
| 1027 | + | |
| 1028 | + | |
| 1029 | + case env_attack: | |
| 1030 | + | |
| 1031 | + // If attack done, time to turn around. | |
| 1032 | + if (adsr->attackPhase > adsr->buff_sizeMinusOne) | |
| 1033 | + { | |
| 1034 | + adsr->whichStage = env_decay; | |
| 1035 | + adsr->next = adsr->gain; | |
| 1036 | + } | |
| 1037 | + else | |
| 1038 | + { | |
| 1039 | + // do interpolation ! | |
| 1040 | + uint32_t intPart = (uint32_t)adsr->attackPhase; | |
| 1041 | + float floatPart = adsr->attackPhase - intPart; | |
| 1042 | + float secondValue; | |
| 1043 | + if (adsr->attackPhase + 1.0f > adsr->buff_sizeMinusOne) | |
| 1044 | + { | |
| 1045 | + secondValue = 0.0f; | |
| 1046 | + } | |
| 1047 | + else | |
| 1048 | + { | |
| 1049 | + secondValue = adsr->exp_buff[(uint32_t)((adsr->attackPhase)+1)]; | |
| 1050 | + } | |
| 1051 | + | |
| 1052 | + adsr->next = adsr->gain * (1.0f - LEAF_interpolation_linear(adsr->exp_buff[intPart], secondValue, floatPart)); // inverted and backwards to get proper rising exponential shape/perception | |
| 1053 | + } | |
| 1054 | + | |
| 1055 | + // Increment ADSR attack. | |
| 1056 | + adsr->attackPhase += adsr->attackInc; | |
| 1057 | + break; | |
| 1058 | + | |
| 1059 | + case env_decay: | |
| 1060 | + | |
| 1061 | + // If decay done, sustain. | |
| 1062 | + if (adsr->decayPhase > adsr->buff_sizeMinusOne) | |
| 1063 | + { | |
| 1064 | + adsr->whichStage = env_sustain; | |
| 1065 | + adsr->next = adsr->gain * adsr->sustain; | |
| 1066 | + } | |
| 1067 | + | |
| 1068 | + else | |
| 1069 | + { | |
| 1070 | + uint32_t intPart = (uint32_t)adsr->decayPhase; | |
| 1071 | + float floatPart = adsr->decayPhase - intPart; | |
| 1072 | + float secondValue; | |
| 1073 | + if (adsr->decayPhase + 1.0f > adsr->buff_sizeMinusOne) | |
| 1074 | + { | |
| 1075 | + secondValue = 0.0f; | |
| 1076 | + } | |
| 1077 | + else | |
| 1078 | + { | |
| 1079 | + secondValue = adsr->exp_buff[(uint32_t)((adsr->decayPhase)+1)]; | |
| 1080 | + } | |
| 1081 | + float interpValue = (LEAF_interpolation_linear(adsr->exp_buff[intPart], secondValue, floatPart)); | |
| 1082 | + adsr->next = (adsr->gain * (adsr->sustain + (interpValue * (1.0f - adsr->sustain)))) * adsr->leakFactor; // do interpolation ! | |
| 1083 | + } | |
| 1084 | + | |
| 1085 | + // Increment ADSR decay. | |
| 1086 | + adsr->decayPhase += adsr->decayInc; | |
| 1087 | + break; | |
| 1088 | + | |
| 1089 | + case env_sustain: | |
| 1090 | + adsr->next = adsr->next * adsr->leakFactor; | |
| 1091 | + break; | |
| 1092 | + | |
| 1093 | + case env_release: | |
| 1094 | + // If release done, finish. | |
| 1095 | + if (adsr->releasePhase > adsr->buff_sizeMinusOne) | |
| 1096 | + { | |
| 1097 | + adsr->whichStage = env_idle; | |
| 1098 | + adsr->next = 0.0f; | |
| 1099 | + } | |
| 1100 | + else { | |
| 1101 | + uint32_t intPart = (uint32_t)adsr->releasePhase; | |
| 1102 | + float floatPart = adsr->releasePhase - intPart; | |
| 1103 | + float secondValue; | |
| 1104 | + if (adsr->releasePhase + 1.0f > adsr->buff_sizeMinusOne) | |
| 1105 | + { | |
| 1106 | + secondValue = 0.0f; | |
| 1107 | + } | |
| 1108 | + else | |
| 1109 | + { | |
| 1110 | + secondValue = adsr->exp_buff[(uint32_t)((adsr->releasePhase)+1)]; | |
| 1111 | + } | |
| 1112 | + adsr->next = adsr->releasePeak * (LEAF_interpolation_linear(adsr->exp_buff[intPart], secondValue, floatPart)); // do interpolation ! | |
| 1113 | + } | |
| 1114 | + | |
| 1115 | + // Increment envelope release; | |
| 1116 | + adsr->releasePhase += adsr->releaseInc; | |
| 1117 | + break; | |
| 1118 | + } | |
| 1119 | + return adsr->next; | |
| 1120 | +} | |
| 1121 | + | |
| 1122 | +float tADSR4_tickNoInterp(tADSR4* const adsrenv) | |
| 1123 | +{ | |
| 1124 | + _tADSR4* adsr = *adsrenv; | |
| 1125 | + | |
| 1126 | + switch (adsr->whichStage) | |
| 1127 | + { | |
| 1128 | + case env_ramp: | |
| 1129 | + if (adsr->rampPhase > adsr->buff_sizeMinusOne) | |
| 1130 | + { | |
| 1131 | + adsr->whichStage = env_attack; | |
| 1132 | + adsr->next = 0.0f; | |
| 1133 | + } | |
| 1134 | + else | |
| 1135 | + { | |
| 1136 | + adsr->next = adsr->rampPeak * adsr->exp_buff[(uint32_t)adsr->rampPhase]; | |
| 1137 | + } | |
| 1138 | + | |
| 1139 | + adsr->rampPhase += adsr->rampInc; | |
| 1140 | + break; | |
| 1141 | + | |
| 1142 | + | |
| 1143 | + case env_attack: | |
| 1144 | + | |
| 1145 | + // If attack done, time to turn around. | |
| 1146 | + if (adsr->attackPhase > adsr->buff_sizeMinusOne) | |
| 1147 | + { | |
| 1148 | + adsr->whichStage = env_decay; | |
| 1149 | + adsr->next = adsr->gain; | |
| 1150 | + } | |
| 1151 | + else | |
| 1152 | + { | |
| 1153 | + adsr->next = adsr->gain * (1.0f - adsr->exp_buff[(uint32_t)adsr->attackPhase]); // inverted and backwards to get proper rising exponential shape/perception | |
| 1154 | + } | |
| 1155 | + | |
| 1156 | + // Increment ADSR attack. | |
| 1157 | + adsr->attackPhase += adsr->attackInc; | |
| 1158 | + break; | |
| 1159 | + | |
| 1160 | + case env_decay: | |
| 1161 | + | |
| 1162 | + // If decay done, sustain. | |
| 1163 | + if (adsr->decayPhase > adsr->buff_sizeMinusOne) | |
| 1164 | + { | |
| 1165 | + adsr->whichStage = env_sustain; | |
| 1166 | + adsr->next = adsr->gain * adsr->sustain; | |
| 1167 | + } | |
| 1168 | + | |
| 1169 | + else | |
| 1170 | + { | |
| 1171 | + adsr->next = (adsr->gain * (adsr->sustain + (adsr->exp_buff[(uint32_t)adsr->decayPhase] * (1.0f - adsr->sustain)))) * adsr->leakFactor; | |
| 1172 | + } | |
| 1173 | + | |
| 1174 | + // Increment ADSR decay. | |
| 1175 | + adsr->decayPhase += adsr->decayInc; | |
| 1176 | + break; | |
| 1177 | + | |
| 1178 | + case env_sustain: | |
| 1179 | + adsr->next = adsr->next * adsr->leakFactor; | |
| 1180 | + break; | |
| 1181 | + | |
| 1182 | + case env_release: | |
| 1183 | + // If release done, finish. | |
| 1184 | + if (adsr->releasePhase > adsr->buff_sizeMinusOne) | |
| 1185 | + { | |
| 1186 | + adsr->whichStage = env_idle; | |
| 1187 | + adsr->next = 0.0f; | |
| 1188 | + } | |
| 1189 | + else { | |
| 1190 | + adsr->next = adsr->releasePeak * adsr->exp_buff[(uint32_t)adsr->releasePhase]; | |
| 1191 | + } | |
| 1192 | + | |
| 1193 | + // Increment envelope release; | |
| 1194 | + adsr->releasePhase += adsr->releaseInc; | |
| 1195 | + break; | |
| 1196 | + } | |
| 1197 | + return adsr->next; | |
| 1198 | +} | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | +/////----------------- | |
| 1203 | +/* Ramp */ | |
| 1204 | +void tRamp_init(tRamp* const r, float time, int samples_per_tick) | |
| 1205 | +{ | |
| 1206 | + _tRamp* ramp = *r = (_tRamp*) leaf_alloc(sizeof(_tRamp)); | |
| 1207 | + | |
| 1208 | + ramp->inv_sr_ms = 1.0f/(leaf.sampleRate*0.001f); | |
| 1209 | + ramp->minimum_time = ramp->inv_sr_ms * samples_per_tick; | |
| 1210 | + ramp->curr = 0.0f; | |
| 1211 | + ramp->dest = 0.0f; | |
| 1212 | + | |
| 1213 | + if (time < ramp->minimum_time) | |
| 1214 | + { | |
| 1215 | + ramp->time = ramp->minimum_time; | |
| 1216 | + } | |
| 1217 | + else | |
| 1218 | + { | |
| 1219 | + ramp->time = time; | |
| 1220 | + } | |
| 1221 | + ramp->factor = (1.0f / ramp->time) * ramp->inv_sr_ms; | |
| 1222 | + ramp->samples_per_tick = samples_per_tick; | |
| 1223 | + ramp->inc = ((ramp->dest - ramp->curr) / ramp->time * ramp->inv_sr_ms) * (float)ramp->samples_per_tick; | |
| 1224 | +} | |
| 1225 | + | |
| 1226 | +void tRamp_free(tRamp* const r) | |
| 1227 | +{ | |
| 1228 | + _tRamp* ramp = *r; | |
| 1229 | + | |
| 1230 | + leaf_free((char*)ramp); | |
| 1231 | +} | |
| 1232 | + | |
| 1233 | +void tRamp_initToPool (tRamp* const r, float time, int samples_per_tick, tMempool* const mp) | |
| 1234 | +{ | |
| 1235 | + _tMempool* m = *mp; | |
| 1236 | + _tRamp* ramp = *r = (_tRamp*) mpool_alloc(sizeof(_tRamp), m); | |
| 1237 | + | |
| 1238 | + ramp->inv_sr_ms = 1.0f/(leaf.sampleRate*0.001f); | |
| 1239 | + ramp->minimum_time = ramp->inv_sr_ms * samples_per_tick; | |
| 1240 | + ramp->curr = 0.0f; | |
| 1241 | + ramp->dest = 0.0f; | |
| 1242 | + | |
| 1243 | + if (time < ramp->minimum_time) | |
| 1244 | + { | |
| 1245 | + ramp->time = ramp->minimum_time; | |
| 1246 | + } | |
| 1247 | + else | |
| 1248 | + { | |
| 1249 | + ramp->time = time; | |
| 1250 | + } | |
| 1251 | + ramp->samples_per_tick = samples_per_tick; | |
| 1252 | + ramp->factor = (1.0f / ramp->time) * ramp->inv_sr_ms * (float)ramp->samples_per_tick; | |
| 1253 | + ramp->inc = (ramp->dest - ramp->curr) * ramp->factor; | |
| 1254 | +} | |
| 1255 | + | |
| 1256 | +void tRamp_freeFromPool (tRamp* const r, tMempool* const mp) | |
| 1257 | +{ | |
| 1258 | + _tMempool* m = *mp; | |
| 1259 | + _tRamp* ramp = *r; | |
| 1260 | + | |
| 1261 | + mpool_free((char*)ramp, m); | |
| 1262 | +} | |
| 1263 | + | |
| 1264 | +void tRamp_setTime(tRamp* const ramp, float time) | |
| 1265 | +{ | |
| 1266 | + _tRamp* r = *ramp; | |
| 1267 | + | |
| 1268 | + if (time < r->minimum_time) | |
| 1269 | + { | |
| 1270 | + r->time = r->minimum_time; | |
| 1271 | + } | |
| 1272 | + else | |
| 1273 | + { | |
| 1274 | + r->time = time; | |
| 1275 | + } | |
| 1276 | + r->factor = (1.0f / r->time) * r->inv_sr_ms * (float)r->samples_per_tick; | |
| 1277 | + r->inc = (r->dest - r->curr) * r->factor; | |
| 1278 | + | |
| 1279 | +} | |
| 1280 | + | |
| 1281 | +void tRamp_setDest(tRamp* const ramp, float dest) | |
| 1282 | +{ | |
| 1283 | + _tRamp* r = *ramp; | |
| 1284 | + r->dest = dest; | |
| 1285 | + r->inc = (r->dest - r->curr) * r->factor; | |
| 1286 | +} | |
| 1287 | + | |
| 1288 | +void tRamp_setVal(tRamp* const ramp, float val) | |
| 1289 | +{ | |
| 1290 | + _tRamp* r = *ramp; | |
| 1291 | + r->curr = val; | |
| 1292 | + r->inc = (r->dest - r->curr) * r->factor; | |
| 1293 | +} | |
| 1294 | + | |
| 1295 | +float tRamp_tick(tRamp* const ramp) | |
| 1296 | +{ | |
| 1297 | + _tRamp* r = *ramp; | |
| 1298 | + | |
| 1299 | + r->curr += r->inc; | |
| 1300 | + | |
| 1301 | + if (((r->curr >= r->dest) && (r->inc > 0.0f)) || ((r->curr <= r->dest) && (r->inc < 0.0f))) | |
| 1302 | + { | |
| 1303 | + r->inc = 0.0f; | |
| 1304 | + r->curr=r->dest; | |
| 1305 | + } | |
| 1306 | + | |
| 1307 | + return r->curr; | |
| 1308 | +} | |
| 1309 | + | |
| 1310 | +float tRamp_sample(tRamp* const ramp) | |
| 1311 | +{ | |
| 1312 | + _tRamp* r = *ramp; | |
| 1313 | + return r->curr; | |
| 1314 | +} | |
| 1315 | + | |
| 1316 | +void tRampSampleRateChanged(tRamp* const ramp) | |
| 1317 | +{ | |
| 1318 | + _tRamp* r = *ramp; | |
| 1319 | + r->inv_sr_ms = 1.0f / (leaf.sampleRate * 0.001f); | |
| 1320 | + r->factor = (1.0f / r->time) * r->inv_sr_ms * (float)r->samples_per_tick; | |
| 1321 | + r->inc = (r->dest - r->curr) * r->factor; | |
| 1322 | +} | |
| 1323 | + | |
| 1324 | + | |
| 1325 | + | |
| 1326 | +/* RampUpDown */ | |
| 1327 | +void tRampUpDown_init(tRampUpDown* const r, float upTime, float downTime, int samples_per_tick) | |
| 1328 | +{ | |
| 1329 | + tRampUpDown_initToPool(r, upTime, downTime, samples_per_tick, &leaf.mempool); | |
| 1330 | +} | |
| 1331 | + | |
| 1332 | +void tRampUpDown_free(tRampUpDown* const r) | |
| 1333 | +{ | |
| 1334 | + tRampUpDown_freeFromPool(r, &leaf.mempool); | |
| 1335 | +} | |
| 1336 | + | |
| 1337 | +void tRampUpDown_initToPool(tRampUpDown* const r, float upTime, float downTime, int samples_per_tick, tMempool* const mp) | |
| 1338 | +{ | |
| 1339 | + _tMempool* m = *mp; | |
| 1340 | + _tRampUpDown* ramp = *r = (_tRampUpDown*) mpool_alloc(sizeof(_tRampUpDown), m); | |
| 1341 | + | |
| 1342 | + ramp->inv_sr_ms = 1.0f/(leaf.sampleRate*0.001f); | |
| 1343 | + ramp->minimum_time = ramp->inv_sr_ms * samples_per_tick; | |
| 1344 | + ramp->curr = 0.0f; | |
| 1345 | + ramp->dest = 0.0f; | |
| 1346 | + | |
| 1347 | + if (upTime < ramp->minimum_time) | |
| 1348 | + { | |
| 1349 | + ramp->upTime = ramp->minimum_time; | |
| 1350 | + } | |
| 1351 | + else | |
| 1352 | + { | |
| 1353 | + ramp->upTime = upTime; | |
| 1354 | + } | |
| 1355 | + | |
| 1356 | + if (downTime < ramp->minimum_time) | |
| 1357 | + { | |
| 1358 | + ramp->downTime = ramp->minimum_time; | |
| 1359 | + } | |
| 1360 | + else | |
| 1361 | + { | |
| 1362 | + ramp->downTime = downTime; | |
| 1363 | + } | |
| 1364 | + | |
| 1365 | + ramp->samples_per_tick = samples_per_tick; | |
| 1366 | + ramp->upInc = ((ramp->dest - ramp->curr) / ramp->upTime * ramp->inv_sr_ms) * (float)ramp->samples_per_tick; | |
| 1367 | + ramp->downInc = ((ramp->dest - ramp->curr) / ramp->downTime * ramp->inv_sr_ms) * (float)ramp->samples_per_tick; | |
| 1368 | +} | |
| 1369 | + | |
| 1370 | +void tRampUpDown_freeFromPool (tRampUpDown* const r, tMempool* const mp) | |
| 1371 | +{ | |
| 1372 | + _tMempool* m = *mp; | |
| 1373 | + _tRampUpDown* ramp = *r; | |
| 1374 | + | |
| 1375 | + mpool_free((char*)ramp, m); | |
| 1376 | +} | |
| 1377 | + | |
| 1378 | +void tRampUpDown_setUpTime(tRampUpDown* const ramp, float upTime) | |
| 1379 | +{ | |
| 1380 | + _tRampUpDown* r = *ramp; | |
| 1381 | + | |
| 1382 | + if (upTime < r->minimum_time) | |
| 1383 | + { | |
| 1384 | + r->upTime = r->minimum_time; | |
| 1385 | + } | |
| 1386 | + else | |
| 1387 | + { | |
| 1388 | + r->upTime = upTime; | |
| 1389 | + } | |
| 1390 | + r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick; | |
| 1391 | +} | |
| 1392 | + | |
| 1393 | + | |
| 1394 | +void tRampUpDown_setDownTime(tRampUpDown* const ramp, float downTime) | |
| 1395 | +{ | |
| 1396 | + _tRampUpDown* r = *ramp; | |
| 1397 | + | |
| 1398 | + if (downTime < r->minimum_time) | |
| 1399 | + { | |
| 1400 | + r->downTime = r->minimum_time; | |
| 1401 | + } | |
| 1402 | + else | |
| 1403 | + { | |
| 1404 | + r->downTime = downTime; | |
| 1405 | + } | |
| 1406 | + r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick; | |
| 1407 | +} | |
| 1408 | + | |
| 1409 | +void tRampUpDown_setDest(tRampUpDown* const ramp, float dest) | |
| 1410 | +{ | |
| 1411 | + _tRampUpDown* r = *ramp; | |
| 1412 | + r->dest = dest; | |
| 1413 | + r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick; | |
| 1414 | + r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick; | |
| 1415 | +} | |
| 1416 | + | |
| 1417 | +void tRampUpDown_setVal(tRampUpDown* const ramp, float val) | |
| 1418 | +{ | |
| 1419 | + _tRampUpDown* r = *ramp; | |
| 1420 | + r->curr = val; | |
| 1421 | + r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick; | |
| 1422 | + r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick; | |
| 1423 | +} | |
| 1424 | + | |
| 1425 | +float tRampUpDown_tick(tRampUpDown* const ramp) | |
| 1426 | +{ | |
| 1427 | + _tRampUpDown* r = *ramp; | |
| 1428 | + float test; | |
| 1429 | + | |
| 1430 | + if (r->dest < r->curr) | |
| 1431 | + { | |
| 1432 | + test = r->curr + r->downInc; | |
| 1433 | + if (test > r->dest) | |
| 1434 | + { | |
| 1435 | + r->curr = test; | |
| 1436 | + } | |
| 1437 | + else | |
| 1438 | + { | |
| 1439 | + r->downInc = 0.0f; | |
| 1440 | + r->curr = r->dest; | |
| 1441 | + } | |
| 1442 | + } | |
| 1443 | + else if (r->dest > r->curr) | |
| 1444 | + { | |
| 1445 | + test = r->curr + r->upInc; | |
| 1446 | + if (test < r->dest) | |
| 1447 | + { | |
| 1448 | + r->curr = test; | |
| 1449 | + } | |
| 1450 | + else | |
| 1451 | + { | |
| 1452 | + r->upInc = 0.0f; | |
| 1453 | + r->curr = r->dest; | |
| 1454 | + } | |
| 1455 | + } | |
| 1456 | + return r->curr; | |
| 1457 | +} | |
| 1458 | + | |
| 1459 | +float tRampUpDown_sample(tRampUpDown* const ramp) | |
| 1460 | +{ | |
| 1461 | + _tRampUpDown* r = *ramp; | |
| 1462 | + return r->curr; | |
| 1463 | +} | |
| 1464 | + | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | +/* Exponential Smoother */ | |
| 1471 | +void tExpSmooth_init(tExpSmooth* const expsmooth, float val, float factor) | |
| 1472 | +{ // factor is usually a value between 0 and 0.1. Lower value is slower. 0.01 for example gives you a smoothing time of about 10ms | |
| 1473 | + _tExpSmooth* smooth = *expsmooth = (_tExpSmooth*) leaf_alloc(sizeof(_tExpSmooth)); | |
| 1474 | + | |
| 1475 | + smooth->curr=val; | |
| 1476 | + smooth->dest=val; | |
| 1477 | + if (factor<0) factor=0; | |
| 1478 | + if (factor>1) factor=1; | |
| 1479 | + smooth->factor=factor; | |
| 1480 | + smooth->oneminusfactor=1.0f-factor; | |
| 1481 | +} | |
| 1482 | + | |
| 1483 | +void tExpSmooth_free(tExpSmooth* const expsmooth) | |
| 1484 | +{ | |
| 1485 | + _tExpSmooth* smooth = *expsmooth; | |
| 1486 | + | |
| 1487 | + leaf_free((char*)smooth); | |
| 1488 | +} | |
| 1489 | + | |
| 1490 | +void tExpSmooth_initToPool (tExpSmooth* const expsmooth, float val, float factor, tMempool* const mp) | |
| 1491 | +{ | |
| 1492 | + _tMempool* m = *mp; | |
| 1493 | + _tExpSmooth* smooth = *expsmooth = (_tExpSmooth*) mpool_alloc(sizeof(_tExpSmooth), m); | |
| 1494 | + | |
| 1495 | + smooth->curr=val; | |
| 1496 | + smooth->dest=val; | |
| 1497 | + if (factor<0) factor=0; | |
| 1498 | + if (factor>1) factor=1; | |
| 1499 | + smooth->factor=factor; | |
| 1500 | + smooth->oneminusfactor=1.0f-factor; | |
| 1501 | +} | |
| 1502 | + | |
| 1503 | +void tExpSmooth_freeFromPool (tExpSmooth* const expsmooth, tMempool* const mp) | |
| 1504 | +{ | |
| 1505 | + _tMempool* m = *mp; | |
| 1506 | + _tExpSmooth* smooth = *expsmooth; | |
| 1507 | + | |
| 1508 | + mpool_free((char*)smooth, m); | |
| 1509 | +} | |
| 1510 | + | |
| 1511 | +void tExpSmooth_setFactor(tExpSmooth* const expsmooth, float factor) | |
| 1512 | +{ // factor is usually a value between 0 and 0.1. Lower value is slower. 0.01 for example gives you a smoothing time of about 10ms | |
| 1513 | + _tExpSmooth* smooth = *expsmooth; | |
| 1514 | + | |
| 1515 | + if (factor<0) | |
| 1516 | + factor=0; | |
| 1517 | + else | |
| 1518 | + if (factor>1) factor=1; | |
| 1519 | + smooth->factor=factor; | |
| 1520 | + smooth->oneminusfactor=1.0f-factor; | |
| 1521 | +} | |
| 1522 | + | |
| 1523 | +void tExpSmooth_setDest(tExpSmooth* const expsmooth, float dest) | |
| 1524 | +{ | |
| 1525 | + _tExpSmooth* smooth = *expsmooth; | |
| 1526 | + smooth->dest=dest; | |
| 1527 | +} | |
| 1528 | + | |
| 1529 | +void tExpSmooth_setVal(tExpSmooth* const expsmooth, float val) | |
| 1530 | +{ | |
| 1531 | + _tExpSmooth* smooth = *expsmooth; | |
| 1532 | + smooth->curr=val; | |
| 1533 | +} | |
| 1534 | + | |
| 1535 | +void tExpSmooth_setValAndDest(tExpSmooth* const expsmooth, float val) | |
| 1536 | +{ | |
| 1537 | + _tExpSmooth* smooth = *expsmooth; | |
| 1538 | + smooth->curr=val; | |
| 1539 | + smooth->dest=val; | |
| 1540 | +} | |
| 1541 | + | |
| 1542 | +float tExpSmooth_tick(tExpSmooth* const expsmooth) | |
| 1543 | +{ | |
| 1544 | + _tExpSmooth* smooth = *expsmooth; | |
| 1545 | + smooth->curr = smooth->factor*smooth->dest+smooth->oneminusfactor*smooth->curr; | |
| 1546 | + return smooth->curr; | |
| 1547 | +} | |
| 1548 | + | |
| 1549 | +float tExpSmooth_sample(tExpSmooth* const expsmooth) | |
| 1550 | +{ | |
| 1551 | + _tExpSmooth* smooth = *expsmooth; | |
| 1552 | + return smooth->curr; | |
| 1553 | +} | |
| 1554 | + | |
| 1555 | +//tSlide is based on the max/msp slide~ object | |
| 1556 | +//// | |
| 1557 | + | |
| 1558 | +void tSlide_init (tSlide* const sl, float upSlide, float downSlide) | |
| 1559 | +{ | |
| 1560 | + tSlide_initToPool (sl, upSlide, downSlide, &leaf.mempool); | |
| 1561 | +} | |
| 1562 | +void tSlide_free (tSlide* const sl) | |
| 1563 | +{ | |
| 1564 | + tSlide_freeFromPool (sl, &leaf.mempool); | |
| 1565 | +} | |
| 1566 | +void tSlide_initToPool (tSlide* const sl, float upSlide, float downSlide, tMempool* const mp) | |
| 1567 | +{ | |
| 1568 | + _tMempool* m = *mp; | |
| 1569 | + _tSlide* s = *sl = (_tSlide*) mpool_alloc(sizeof(_tSlide), m); | |
| 1570 | + s->prevIn = 0.0f; | |
| 1571 | + s->currentOut = 0.0f; | |
| 1572 | + s->prevOut = 0.0f; | |
| 1573 | + if (upSlide < 1.0f) | |
| 1574 | + { | |
| 1575 | + upSlide = 1.0f; | |
| 1576 | + } | |
| 1577 | + | |
| 1578 | + if (downSlide < 1.0f) | |
| 1579 | + { | |
| 1580 | + downSlide = 1.0f; | |
| 1581 | + } | |
| 1582 | + s->invUpSlide = 1.0f / upSlide; | |
| 1583 | + s->invDownSlide = 1.0f / downSlide; | |
| 1584 | +} | |
| 1585 | + | |
| 1586 | +void tSlide_freeFromPool (tSlide* const sl, tMempool* const mp) | |
| 1587 | +{ | |
| 1588 | + _tMempool* m = *mp; | |
| 1589 | + _tSlide* s = *sl; | |
| 1590 | + | |
| 1591 | + mpool_free((char*)s, m); | |
| 1592 | +} | |
| 1593 | + | |
| 1594 | +float tSlide_tick(tSlide* const sl, float in) | |
| 1595 | +{ | |
| 1596 | + _tSlide* s = *sl; | |
| 1597 | + | |
| 1598 | + | |
| 1599 | + if (in >= s->prevOut) | |
| 1600 | + { | |
| 1601 | + s->currentOut = s->prevOut + ((in - s->prevOut) * s->invUpSlide); | |
| 1602 | + } | |
| 1603 | + else | |
| 1604 | + { | |
| 1605 | + s->currentOut = s->prevOut + ((in - s->prevOut) * s->invDownSlide); | |
| 1606 | + } | |
| 1607 | + if (s->currentOut < VSF) s->currentOut = 0.0f; | |
| 1608 | + s->prevIn = in; | |
| 1609 | + s->prevOut = s->currentOut; | |
| 1610 | + return s->currentOut; | |
| 1611 | +} | |
| 1612 | + | |
| @@ -0,0 +1,1802 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-filter.c | |
| 4 | + Created: 20 Jan 2017 12:01:10pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-filters.h" | |
| 12 | +#include "..\Inc\leaf-tables.h" | |
| 13 | +#include "..\leaf.h" | |
| 14 | + | |
| 15 | +#else | |
| 16 | + | |
| 17 | +#include "../Inc/leaf-filters.h" | |
| 18 | +#include "../Inc/leaf-tables.h" | |
| 19 | +#include "../leaf.h" | |
| 20 | +//#include "tim.h" | |
| 21 | +#endif | |
| 22 | + | |
| 23 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ OnePole Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 24 | +void tAllpass_init(tAllpass* const ft, float initDelay, uint32_t maxDelay) | |
| 25 | +{ | |
| 26 | + _tAllpass* f = *ft = (_tAllpass*) leaf_alloc(sizeof(_tAllpass)); | |
| 27 | + | |
| 28 | + f->gain = 0.7f; | |
| 29 | + | |
| 30 | + f->lastOut = 0.0f; | |
| 31 | + | |
| 32 | + tLinearDelay_init(&f->delay, initDelay, maxDelay); | |
| 33 | +} | |
| 34 | + | |
| 35 | +void tAllpass_free(tAllpass* const ft) | |
| 36 | +{ | |
| 37 | + _tAllpass* f = *ft; | |
| 38 | + | |
| 39 | + tLinearDelay_free(&f->delay); | |
| 40 | + leaf_free((char*)f); | |
| 41 | +} | |
| 42 | + | |
| 43 | +void tAllpass_initToPool (tAllpass* const ft, float initDelay, uint32_t maxDelay, tMempool* const mp) | |
| 44 | +{ | |
| 45 | + _tMempool* m = *mp; | |
| 46 | + _tAllpass* f = *ft = (_tAllpass*) mpool_alloc(sizeof(_tAllpass), m); | |
| 47 | + | |
| 48 | + f->gain = 0.7f; | |
| 49 | + | |
| 50 | + f->lastOut = 0.0f; | |
| 51 | + | |
| 52 | + tLinearDelay_initToPool(&f->delay, initDelay, maxDelay, mp); | |
| 53 | +} | |
| 54 | + | |
| 55 | +void tAllpass_freeFromPool (tAllpass* const ft, tMempool* const mp) | |
| 56 | +{ | |
| 57 | + _tMempool* m = *mp; | |
| 58 | + _tAllpass* f = *ft; | |
| 59 | + | |
| 60 | + tLinearDelay_freeFromPool(&f->delay, mp); | |
| 61 | + mpool_free((char*)f, m); | |
| 62 | +} | |
| 63 | + | |
| 64 | +void tAllpass_setDelay(tAllpass* const ft, float delay) | |
| 65 | +{ | |
| 66 | + _tAllpass* f = *ft; | |
| 67 | + | |
| 68 | + tLinearDelay_setDelay(&f->delay, delay); | |
| 69 | +} | |
| 70 | + | |
| 71 | +void tAllpass_setGain(tAllpass* const ft, float gain) | |
| 72 | +{ | |
| 73 | + _tAllpass* f = *ft; | |
| 74 | + | |
| 75 | + f->gain = gain; | |
| 76 | +} | |
| 77 | + | |
| 78 | +float tAllpass_tick(tAllpass* const ft, float input) | |
| 79 | +{ | |
| 80 | + _tAllpass* f = *ft; | |
| 81 | + | |
| 82 | + float s1 = (-f->gain) * f->lastOut + input; | |
| 83 | + | |
| 84 | + float s2 = tLinearDelay_tick(&f->delay, s1) + (f->gain) * input; | |
| 85 | + | |
| 86 | + f->lastOut = s2; | |
| 87 | + | |
| 88 | + return f->lastOut; | |
| 89 | +} | |
| 90 | + | |
| 91 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ OnePole Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 92 | +void tOnePole_init(tOnePole* const ft, float freq) | |
| 93 | +{ | |
| 94 | + _tOnePole* f = *ft = (_tOnePole*) leaf_alloc(sizeof(_tOnePole)); | |
| 95 | + | |
| 96 | + f->gain = 1.0f; | |
| 97 | + f->a0 = 1.0; | |
| 98 | + | |
| 99 | + tOnePole_setFreq(ft, freq); | |
| 100 | + | |
| 101 | + f->lastIn = 0.0f; | |
| 102 | + f->lastOut = 0.0f; | |
| 103 | +} | |
| 104 | + | |
| 105 | +void tOnePole_free(tOnePole* const ft) | |
| 106 | +{ | |
| 107 | + _tOnePole* f = *ft; | |
| 108 | + | |
| 109 | + leaf_free((char*)f); | |
| 110 | +} | |
| 111 | + | |
| 112 | +void tOnePole_initToPool (tOnePole* const ft, float freq, tMempool* const mp) | |
| 113 | +{ | |
| 114 | + _tMempool* m = *mp; | |
| 115 | + _tOnePole* f = *ft = (_tOnePole*) mpool_alloc(sizeof(_tOnePole), m); | |
| 116 | + | |
| 117 | + f->gain = 1.0f; | |
| 118 | + f->a0 = 1.0; | |
| 119 | + | |
| 120 | + tOnePole_setFreq(ft, freq); | |
| 121 | + | |
| 122 | + f->lastIn = 0.0f; | |
| 123 | + f->lastOut = 0.0f; | |
| 124 | +} | |
| 125 | + | |
| 126 | +void tOnePole_freeFromPool (tOnePole* const ft, tMempool* const mp) | |
| 127 | +{ | |
| 128 | + _tMempool* m = *mp; | |
| 129 | + _tOnePole* f = *ft; | |
| 130 | + | |
| 131 | + mpool_free((char*)f, m); | |
| 132 | +} | |
| 133 | + | |
| 134 | +void tOnePole_setB0(tOnePole* const ft, float b0) | |
| 135 | +{ | |
| 136 | + _tOnePole* f = *ft; | |
| 137 | + f->b0 = b0; | |
| 138 | +} | |
| 139 | + | |
| 140 | +void tOnePole_setA1(tOnePole* const ft, float a1) | |
| 141 | +{ | |
| 142 | + _tOnePole* f = *ft; | |
| 143 | + if (a1 >= 1.0f) a1 = 0.999999f; | |
| 144 | + f->a1 = a1; | |
| 145 | +} | |
| 146 | + | |
| 147 | +void tOnePole_setPole(tOnePole* const ft, float thePole) | |
| 148 | +{ | |
| 149 | + _tOnePole* f = *ft; | |
| 150 | + | |
| 151 | + if (thePole >= 1.0f) thePole = 0.999999f; | |
| 152 | + | |
| 153 | + // Normalize coefficients for peak unity gain. | |
| 154 | + if (thePole > 0.0f) f->b0 = (1.0f - thePole); | |
| 155 | + else f->b0 = (1.0f + thePole); | |
| 156 | + | |
| 157 | + f->a1 = -thePole; | |
| 158 | +} | |
| 159 | + | |
| 160 | +void tOnePole_setFreq (tOnePole* const ft, float freq) | |
| 161 | +{ | |
| 162 | + _tOnePole* f = *ft; | |
| 163 | + f->b0 = freq * leaf.twoPiTimesInvSampleRate; | |
| 164 | + f->b0 = LEAF_clip(0.0f, f->b0, 1.0f); | |
| 165 | + f->a1 = 1.0f - f->b0; | |
| 166 | +} | |
| 167 | + | |
| 168 | +void tOnePole_setCoefficients(tOnePole* const ft, float b0, float a1) | |
| 169 | +{ | |
| 170 | + _tOnePole* f = *ft; | |
| 171 | + if (a1 >= 1.0f) a1 = 0.999999f; | |
| 172 | + f->b0 = b0; | |
| 173 | + f->a1 = a1; | |
| 174 | +} | |
| 175 | + | |
| 176 | +void tOnePole_setGain(tOnePole* const ft, float gain) | |
| 177 | +{ | |
| 178 | + _tOnePole* f = *ft; | |
| 179 | + f->gain = gain; | |
| 180 | +} | |
| 181 | + | |
| 182 | +float tOnePole_tick(tOnePole* const ft, float input) | |
| 183 | +{ | |
| 184 | + _tOnePole* f = *ft; | |
| 185 | + | |
| 186 | + float in = input * f->gain; | |
| 187 | + float out = (f->b0 * in) + (f->a1 * f->lastOut); | |
| 188 | + | |
| 189 | + f->lastIn = in; | |
| 190 | + f->lastOut = out; | |
| 191 | + | |
| 192 | + return out; | |
| 193 | +} | |
| 194 | + | |
| 195 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ TwoPole Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 196 | +void tTwoPole_init(tTwoPole* const ft) | |
| 197 | +{ | |
| 198 | + _tTwoPole* f = *ft = (_tTwoPole*) leaf_alloc(sizeof(_tTwoPole)); | |
| 199 | + | |
| 200 | + f->gain = 1.0f; | |
| 201 | + f->a0 = 1.0; | |
| 202 | + f->b0 = 1.0; | |
| 203 | + | |
| 204 | + f->lastOut[0] = 0.0f; | |
| 205 | + f->lastOut[1] = 0.0f; | |
| 206 | +} | |
| 207 | + | |
| 208 | +void tTwoPole_free(tTwoPole* const ft) | |
| 209 | +{ | |
| 210 | + _tTwoPole* f = *ft; | |
| 211 | + | |
| 212 | + leaf_free((char*)f); | |
| 213 | +} | |
| 214 | + | |
| 215 | +void tTwoPole_initToPool (tTwoPole* const ft, tMempool* const mp) | |
| 216 | +{ | |
| 217 | + _tMempool* m = *mp; | |
| 218 | + _tTwoPole* f = *ft = (_tTwoPole*) mpool_alloc(sizeof(_tTwoPole), m); | |
| 219 | + | |
| 220 | + f->gain = 1.0f; | |
| 221 | + f->a0 = 1.0; | |
| 222 | + f->b0 = 1.0; | |
| 223 | + | |
| 224 | + f->lastOut[0] = 0.0f; | |
| 225 | + f->lastOut[1] = 0.0f; | |
| 226 | +} | |
| 227 | + | |
| 228 | +void tTwoPole_freeFromPool (tTwoPole* const ft, tMempool* const mp) | |
| 229 | +{ | |
| 230 | + _tMempool* m = *mp; | |
| 231 | + _tTwoPole* f = *ft; | |
| 232 | + | |
| 233 | + mpool_free((char*)f, m); | |
| 234 | +} | |
| 235 | + | |
| 236 | +float tTwoPole_tick(tTwoPole* const ft, float input) | |
| 237 | +{ | |
| 238 | + _tTwoPole* f = *ft; | |
| 239 | + | |
| 240 | + float in = input * f->gain; | |
| 241 | + float out = (f->b0 * in) - (f->a1 * f->lastOut[0]) - (f->a2 * f->lastOut[1]); | |
| 242 | + | |
| 243 | + f->lastOut[1] = f->lastOut[0]; | |
| 244 | + f->lastOut[0] = out; | |
| 245 | + | |
| 246 | + return out; | |
| 247 | +} | |
| 248 | + | |
| 249 | +void tTwoPole_setB0(tTwoPole* const ft, float b0) | |
| 250 | +{ | |
| 251 | + _tTwoPole* f = *ft; | |
| 252 | + f->b0 = b0; | |
| 253 | +} | |
| 254 | + | |
| 255 | +void tTwoPole_setA1(tTwoPole* const ft, float a1) | |
| 256 | +{ | |
| 257 | + _tTwoPole* f = *ft; | |
| 258 | + f->a1 = a1; | |
| 259 | +} | |
| 260 | + | |
| 261 | +void tTwoPole_setA2(tTwoPole* const ft, float a2) | |
| 262 | +{ | |
| 263 | + _tTwoPole* f = *ft; | |
| 264 | + f->a2 = a2; | |
| 265 | +} | |
| 266 | + | |
| 267 | + | |
| 268 | +void tTwoPole_setResonance(tTwoPole* const ft, float frequency, float radius, oBool normalize) | |
| 269 | +{ | |
| 270 | + _tTwoPole* f = *ft; | |
| 271 | + | |
| 272 | + if (frequency < 0.0f) frequency = 0.0f; | |
| 273 | + if (frequency > (leaf.sampleRate * 0.49f)) frequency = leaf.sampleRate * 0.49f; | |
| 274 | + if (radius < 0.0f) radius = 0.0f; | |
| 275 | + if (radius >= 1.0f) radius = 0.999999f; | |
| 276 | + | |
| 277 | + f->radius = radius; | |
| 278 | + f->frequency = frequency; | |
| 279 | + f->normalize = normalize; | |
| 280 | + | |
| 281 | + f->a2 = radius * radius; | |
| 282 | + f->a1 = -2.0f * radius * cosf(frequency * leaf.twoPiTimesInvSampleRate); | |
| 283 | + | |
| 284 | + if ( normalize ) | |
| 285 | + { | |
| 286 | + // Normalize the filter gain ... not terribly efficient. | |
| 287 | + float real = 1 - radius + (f->a2 - radius) * cosf(2 * frequency * leaf.twoPiTimesInvSampleRate); | |
| 288 | + float imag = (f->a2 - radius) * sinf(2 * frequency * leaf.twoPiTimesInvSampleRate); | |
| 289 | + f->b0 = sqrtf( powf(real, 2) + powf(imag, 2) ); | |
| 290 | + } | |
| 291 | +} | |
| 292 | + | |
| 293 | +void tTwoPole_setCoefficients(tTwoPole* const ft, float b0, float a1, float a2) | |
| 294 | +{ | |
| 295 | + _tTwoPole* f = *ft; | |
| 296 | + f->b0 = b0; | |
| 297 | + f->a1 = a1; | |
| 298 | + f->a2 = a2; | |
| 299 | +} | |
| 300 | + | |
| 301 | +void tTwoPole_setGain(tTwoPole* const ft, float gain) | |
| 302 | +{ | |
| 303 | + _tTwoPole* f = *ft; | |
| 304 | + f->gain = gain; | |
| 305 | +} | |
| 306 | + | |
| 307 | +void tTwoPoleSampleRateChanged (tTwoPole* const ft) | |
| 308 | +{ | |
| 309 | + _tTwoPole* f = *ft; | |
| 310 | + | |
| 311 | + f->a2 = f->radius * f->radius; | |
| 312 | + f->a1 = -2.0f * f->radius * cosf(f->frequency * leaf.twoPiTimesInvSampleRate); | |
| 313 | + | |
| 314 | + if ( f->normalize ) | |
| 315 | + { | |
| 316 | + // Normalize the filter gain ... not terribly efficient. | |
| 317 | + float real = 1 - f->radius + (f->a2 - f->radius) * cosf(2 * f->frequency * leaf.twoPiTimesInvSampleRate); | |
| 318 | + float imag = (f->a2 - f->radius) * sinf(2 * f->frequency * leaf.twoPiTimesInvSampleRate); | |
| 319 | + f->b0 = sqrtf( powf(real, 2) + powf(imag, 2) ); | |
| 320 | + } | |
| 321 | +} | |
| 322 | + | |
| 323 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ OneZero Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 324 | +void tOneZero_init(tOneZero* const ft, float theZero) | |
| 325 | +{ | |
| 326 | + _tOneZero* f = *ft = (_tOneZero*) leaf_alloc(sizeof(_tOneZero)); | |
| 327 | + | |
| 328 | + f->gain = 1.0f; | |
| 329 | + f->lastIn = 0.0f; | |
| 330 | + f->lastOut = 0.0f; | |
| 331 | + tOneZero_setZero(ft, theZero); | |
| 332 | +} | |
| 333 | + | |
| 334 | +void tOneZero_free(tOneZero* const ft) | |
| 335 | +{ | |
| 336 | + _tOneZero* f = *ft; | |
| 337 | + | |
| 338 | + leaf_free((char*)f); | |
| 339 | +} | |
| 340 | + | |
| 341 | +void tOneZero_initToPool (tOneZero* const ft, float theZero, tMempool* const mp) | |
| 342 | +{ | |
| 343 | + _tMempool* m = *mp; | |
| 344 | + _tOneZero* f = *ft = (_tOneZero*) mpool_alloc(sizeof(_tOneZero), m); | |
| 345 | + | |
| 346 | + f->gain = 1.0f; | |
| 347 | + f->lastIn = 0.0f; | |
| 348 | + f->lastOut = 0.0f; | |
| 349 | + tOneZero_setZero(ft, theZero); | |
| 350 | +} | |
| 351 | + | |
| 352 | +void tOneZero_freeFromPool (tOneZero* const ft, tMempool* const mp) | |
| 353 | +{ | |
| 354 | + _tMempool* m = *mp; | |
| 355 | + _tOneZero* f = *ft; | |
| 356 | + | |
| 357 | + mpool_free((char*)f, m); | |
| 358 | +} | |
| 359 | + | |
| 360 | +float tOneZero_tick(tOneZero* const ft, float input) | |
| 361 | +{ | |
| 362 | + _tOneZero* f = *ft; | |
| 363 | + | |
| 364 | + float in = input * f->gain; | |
| 365 | + float out = f->b1 * f->lastIn + f->b0 * in; | |
| 366 | + | |
| 367 | + f->lastIn = in; | |
| 368 | + | |
| 369 | + return out; | |
| 370 | +} | |
| 371 | + | |
| 372 | +void tOneZero_setZero(tOneZero* const ft, float theZero) | |
| 373 | +{ | |
| 374 | + _tOneZero* f = *ft; | |
| 375 | + | |
| 376 | + if (theZero > 0.0f) f->b0 = 1.0f / (1.0f + theZero); | |
| 377 | + else f->b0 = 1.0f / (1.0f - theZero); | |
| 378 | + | |
| 379 | + f->b1 = -theZero * f->b0; | |
| 380 | + | |
| 381 | +} | |
| 382 | + | |
| 383 | +void tOneZero_setB0(tOneZero* const ft, float b0) | |
| 384 | +{ | |
| 385 | + _tOneZero* f = *ft; | |
| 386 | + | |
| 387 | + f->b0 = b0; | |
| 388 | +} | |
| 389 | + | |
| 390 | +void tOneZero_setB1(tOneZero* const ft, float b1) | |
| 391 | +{ | |
| 392 | + _tOneZero* f = *ft; | |
| 393 | + f->b1 = b1; | |
| 394 | +} | |
| 395 | + | |
| 396 | +void tOneZero_setCoefficients(tOneZero* const ft, float b0, float b1) | |
| 397 | +{ | |
| 398 | + _tOneZero* f = *ft; | |
| 399 | + f->b0 = b0; | |
| 400 | + f->b1 = b1; | |
| 401 | +} | |
| 402 | + | |
| 403 | +void tOneZero_setGain(tOneZero *ft, float gain) | |
| 404 | +{ | |
| 405 | + _tOneZero* f = *ft; | |
| 406 | + f->gain = gain; | |
| 407 | +} | |
| 408 | + | |
| 409 | +float tOneZero_getPhaseDelay(tOneZero* const ft, float frequency ) | |
| 410 | +{ | |
| 411 | + _tOneZero* f = *ft; | |
| 412 | + | |
| 413 | + if ( frequency <= 0.0f) frequency = 0.05f; | |
| 414 | + | |
| 415 | + f->frequency = frequency; | |
| 416 | + | |
| 417 | + float omegaT = 2 * PI * frequency * leaf.invSampleRate; | |
| 418 | + float real = 0.0, imag = 0.0; | |
| 419 | + | |
| 420 | + real += f->b0; | |
| 421 | + | |
| 422 | + real += f->b1 * cosf(omegaT); | |
| 423 | + imag -= f->b1 * sinf(omegaT); | |
| 424 | + | |
| 425 | + real *= f->gain; | |
| 426 | + imag *= f->gain; | |
| 427 | + | |
| 428 | + float phase = atan2f( imag, real ); | |
| 429 | + | |
| 430 | + real = 0.0; imag = 0.0; | |
| 431 | + | |
| 432 | + phase -= atan2f( imag, real ); | |
| 433 | + | |
| 434 | + phase = fmodf( -phase, 2 * PI ); | |
| 435 | + | |
| 436 | + return phase / omegaT; | |
| 437 | +} | |
| 438 | + | |
| 439 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ TwoZero Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 440 | +void tTwoZero_init(tTwoZero* const ft) | |
| 441 | +{ | |
| 442 | + _tTwoZero* f = *ft = (_tTwoZero*) leaf_alloc(sizeof(_tTwoZero)); | |
| 443 | + | |
| 444 | + f->gain = 1.0f; | |
| 445 | + f->lastIn[0] = 0.0f; | |
| 446 | + f->lastIn[1] = 0.0f; | |
| 447 | +} | |
| 448 | + | |
| 449 | +void tTwoZero_free(tTwoZero* const ft) | |
| 450 | +{ | |
| 451 | + _tTwoZero* f = *ft; | |
| 452 | + | |
| 453 | + leaf_free((char*)f); | |
| 454 | +} | |
| 455 | + | |
| 456 | +void tTwoZero_initToPool (tTwoZero* const ft, tMempool* const mp) | |
| 457 | +{ | |
| 458 | + _tMempool* m = *mp; | |
| 459 | + _tTwoZero* f = *ft = (_tTwoZero*) mpool_alloc(sizeof(_tTwoZero), m); | |
| 460 | + | |
| 461 | + f->gain = 1.0f; | |
| 462 | + f->lastIn[0] = 0.0f; | |
| 463 | + f->lastIn[1] = 0.0f; | |
| 464 | +} | |
| 465 | + | |
| 466 | +void tTwoZero_freeFromPool (tTwoZero* const ft, tMempool* const mp) | |
| 467 | +{ | |
| 468 | + _tMempool* m = *mp; | |
| 469 | + _tTwoZero* f = *ft; | |
| 470 | + | |
| 471 | + mpool_free((char*)f, m); | |
| 472 | +} | |
| 473 | + | |
| 474 | +float tTwoZero_tick(tTwoZero* const ft, float input) | |
| 475 | +{ | |
| 476 | + _tTwoZero* f = *ft; | |
| 477 | + | |
| 478 | + float in = input * f->gain; | |
| 479 | + float out = f->b2 * f->lastIn[1] + f->b1 * f->lastIn[0] + f->b0 * in; | |
| 480 | + | |
| 481 | + f->lastIn[1] = f->lastIn[0]; | |
| 482 | + f->lastIn[0] = in; | |
| 483 | + | |
| 484 | + return out; | |
| 485 | +} | |
| 486 | + | |
| 487 | +void tTwoZero_setNotch(tTwoZero* const ft, float freq, float radius) | |
| 488 | +{ | |
| 489 | + _tTwoZero* f = *ft; | |
| 490 | + | |
| 491 | + // Should also deal with frequency being > half sample rate / nyquist. See STK | |
| 492 | + if (freq < 0.0f) freq = 0.0f; | |
| 493 | + if (radius < 0.0f) radius = 0.0f; | |
| 494 | + | |
| 495 | + f->frequency = freq; | |
| 496 | + f->radius = radius; | |
| 497 | + | |
| 498 | + f->b2 = radius * radius; | |
| 499 | + f->b1 = -2.0f * radius * cosf(freq * leaf.twoPiTimesInvSampleRate); // OPTIMIZE with LOOKUP or APPROXIMATION | |
| 500 | + | |
| 501 | + // Normalize the filter gain. From STK. | |
| 502 | + if ( f->b1 > 0.0f ) // Maximum at z = 0. | |
| 503 | + f->b0 = 1.0f / ( 1.0f + f->b1 + f->b2 ); | |
| 504 | + else // Maximum at z = -1. | |
| 505 | + f->b0 = 1.0f / ( 1.0f - f->b1 + f->b2 ); | |
| 506 | + f->b1 *= f->b0; | |
| 507 | + f->b2 *= f->b0; | |
| 508 | + | |
| 509 | +} | |
| 510 | + | |
| 511 | +void tTwoZero_setB0(tTwoZero* const ft, float b0) | |
| 512 | +{ | |
| 513 | + _tTwoZero* f = *ft; | |
| 514 | + f->b0 = b0; | |
| 515 | +} | |
| 516 | + | |
| 517 | +void tTwoZero_setB1(tTwoZero* const ft, float b1) | |
| 518 | +{ | |
| 519 | + _tTwoZero* f = *ft; | |
| 520 | + f->b1 = b1; | |
| 521 | +} | |
| 522 | + | |
| 523 | +void tTwoZero_setCoefficients(tTwoZero* const ft, float b0, float b1, float b2) | |
| 524 | +{ | |
| 525 | + _tTwoZero* f = *ft; | |
| 526 | + f->b0 = b0; | |
| 527 | + f->b1 = b1; | |
| 528 | + f->b2 = b2; | |
| 529 | +} | |
| 530 | + | |
| 531 | +void tTwoZero_setGain(tTwoZero* const ft, float gain) | |
| 532 | +{ | |
| 533 | + _tTwoZero* f = *ft; | |
| 534 | + f->gain = gain; | |
| 535 | +} | |
| 536 | + | |
| 537 | +void tTwoZeroSampleRateChanged(tTwoZero* const ft) | |
| 538 | +{ | |
| 539 | + _tTwoZero* f = *ft; | |
| 540 | + | |
| 541 | + tTwoZero_setNotch(ft, f->frequency, f->radius); | |
| 542 | +} | |
| 543 | + | |
| 544 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ PoleZero Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 545 | +void tPoleZero_init(tPoleZero* const pzf) | |
| 546 | +{ | |
| 547 | + _tPoleZero* f = *pzf = (_tPoleZero*) leaf_alloc(sizeof(_tPoleZero)); | |
| 548 | + | |
| 549 | + f->gain = 1.0f; | |
| 550 | + f->b0 = 1.0; | |
| 551 | + f->a0 = 1.0; | |
| 552 | + | |
| 553 | + f->lastIn = 0.0f; | |
| 554 | + f->lastOut = 0.0f; | |
| 555 | +} | |
| 556 | + | |
| 557 | +void tPoleZero_free(tPoleZero* const pzf) | |
| 558 | +{ | |
| 559 | + _tPoleZero* f = *pzf; | |
| 560 | + | |
| 561 | + leaf_free((char*)f); | |
| 562 | +} | |
| 563 | + | |
| 564 | +void tPoleZero_initToPool (tPoleZero* const pzf, tMempool* const mp) | |
| 565 | +{ | |
| 566 | + _tMempool* m = *mp; | |
| 567 | + _tPoleZero* f = *pzf = (_tPoleZero*) mpool_alloc(sizeof(_tPoleZero), m); | |
| 568 | + | |
| 569 | + f->gain = 1.0f; | |
| 570 | + f->b0 = 1.0; | |
| 571 | + f->a0 = 1.0; | |
| 572 | + | |
| 573 | + f->lastIn = 0.0f; | |
| 574 | + f->lastOut = 0.0f; | |
| 575 | +} | |
| 576 | + | |
| 577 | +void tPoleZero_freeFromPool (tPoleZero* const pzf, tMempool* const mp) | |
| 578 | +{ | |
| 579 | + _tMempool* m = *mp; | |
| 580 | + _tPoleZero* f = *pzf; | |
| 581 | + | |
| 582 | + mpool_free((char*)f, m); | |
| 583 | +} | |
| 584 | + | |
| 585 | +void tPoleZero_setB0(tPoleZero* const pzf, float b0) | |
| 586 | +{ | |
| 587 | + _tPoleZero* f = *pzf; | |
| 588 | + f->b0 = b0; | |
| 589 | +} | |
| 590 | + | |
| 591 | +void tPoleZero_setB1(tPoleZero* const pzf, float b1) | |
| 592 | +{ | |
| 593 | + _tPoleZero* f = *pzf; | |
| 594 | + f->b1 = b1; | |
| 595 | +} | |
| 596 | + | |
| 597 | +void tPoleZero_setA1(tPoleZero* const pzf, float a1) | |
| 598 | +{ | |
| 599 | + _tPoleZero* f = *pzf; | |
| 600 | + | |
| 601 | + if (a1 >= 1.0f) // a1 should be less than 1.0 | |
| 602 | + { | |
| 603 | + a1 = 0.999999f; | |
| 604 | + } | |
| 605 | + | |
| 606 | + f->a1 = a1; | |
| 607 | +} | |
| 608 | + | |
| 609 | +void tPoleZero_setCoefficients(tPoleZero* const pzf, float b0, float b1, float a1) | |
| 610 | +{ | |
| 611 | + _tPoleZero* f = *pzf; | |
| 612 | + | |
| 613 | + if (a1 >= 1.0f) // a1 should be less than 1.0 | |
| 614 | + { | |
| 615 | + a1 = 0.999999f; | |
| 616 | + } | |
| 617 | + | |
| 618 | + f->b0 = b0; | |
| 619 | + f->b1 = b1; | |
| 620 | + f->a1 = a1; | |
| 621 | +} | |
| 622 | + | |
| 623 | +void tPoleZero_setAllpass(tPoleZero* const pzf, float coeff) | |
| 624 | +{ | |
| 625 | + _tPoleZero* f = *pzf; | |
| 626 | + | |
| 627 | + if (coeff >= 1.0f) // allpass coefficient >= 1.0 makes filter unstable | |
| 628 | + { | |
| 629 | + coeff = 0.999999f; | |
| 630 | + } | |
| 631 | + | |
| 632 | + f->b0 = coeff; | |
| 633 | + f->b1 = 1.0f; | |
| 634 | + f->a0 = 1.0f; | |
| 635 | + f->a1 = coeff; | |
| 636 | +} | |
| 637 | + | |
| 638 | +void tPoleZero_setBlockZero(tPoleZero* const pzf, float thePole) | |
| 639 | +{ | |
| 640 | + _tPoleZero* f = *pzf; | |
| 641 | + | |
| 642 | + if (thePole >= 1.0f) // allpass coefficient >= 1.0 makes filter unstable | |
| 643 | + { | |
| 644 | + thePole = 0.999999f; | |
| 645 | + } | |
| 646 | + | |
| 647 | + f->b0 = 1.0f; | |
| 648 | + f->b1 = -1.0f; | |
| 649 | + f->a0 = 1.0f; | |
| 650 | + f->a1 = -thePole; | |
| 651 | +} | |
| 652 | + | |
| 653 | +void tPoleZero_setGain(tPoleZero* const pzf, float gain) | |
| 654 | +{ | |
| 655 | + _tPoleZero* f = *pzf; | |
| 656 | + f->gain = gain; | |
| 657 | +} | |
| 658 | + | |
| 659 | +float tPoleZero_tick(tPoleZero* const pzf, float input) | |
| 660 | +{ | |
| 661 | + _tPoleZero* f = *pzf; | |
| 662 | + | |
| 663 | + float in = input * f->gain; | |
| 664 | + float out = (f->b0 * in) + (f->b1 * f->lastIn) - (f->a1 * f->lastOut); | |
| 665 | + | |
| 666 | + f->lastIn = in; | |
| 667 | + f->lastOut = out; | |
| 668 | + | |
| 669 | + return out; | |
| 670 | +} | |
| 671 | + | |
| 672 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ BiQuad Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 673 | +void tBiQuad_init(tBiQuad* const ft) | |
| 674 | +{ | |
| 675 | + _tBiQuad* f = *ft = (_tBiQuad*) leaf_alloc(sizeof(_tBiQuad)); | |
| 676 | + | |
| 677 | + f->gain = 1.0f; | |
| 678 | + | |
| 679 | + f->b0 = 0.0f; | |
| 680 | + f->a0 = 0.0f; | |
| 681 | + | |
| 682 | + f->lastIn[0] = 0.0f; | |
| 683 | + f->lastIn[1] = 0.0f; | |
| 684 | + f->lastOut[0] = 0.0f; | |
| 685 | + f->lastOut[1] = 0.0f; | |
| 686 | +} | |
| 687 | + | |
| 688 | +void tBiQuad_free(tBiQuad* const ft) | |
| 689 | +{ | |
| 690 | + _tBiQuad* f = *ft; | |
| 691 | + | |
| 692 | + leaf_free((char*)f); | |
| 693 | +} | |
| 694 | + | |
| 695 | +void tBiQuad_initToPool (tBiQuad* const ft, tMempool* const mp) | |
| 696 | +{ | |
| 697 | + _tMempool* m = *mp; | |
| 698 | + _tBiQuad* f = *ft = (_tBiQuad*) mpool_alloc(sizeof(_tBiQuad), m); | |
| 699 | + | |
| 700 | + f->gain = 1.0f; | |
| 701 | + | |
| 702 | + f->b0 = 0.0f; | |
| 703 | + f->a0 = 0.0f; | |
| 704 | + | |
| 705 | + f->lastIn[0] = 0.0f; | |
| 706 | + f->lastIn[1] = 0.0f; | |
| 707 | + f->lastOut[0] = 0.0f; | |
| 708 | + f->lastOut[1] = 0.0f; | |
| 709 | +} | |
| 710 | + | |
| 711 | +void tBiQuad_freeFromPool (tBiQuad* const ft, tMempool* const mp) | |
| 712 | +{ | |
| 713 | + _tMempool* m = *mp; | |
| 714 | + _tBiQuad* f = *ft; | |
| 715 | + | |
| 716 | + mpool_free((char*)f, m); | |
| 717 | +} | |
| 718 | + | |
| 719 | +float tBiQuad_tick(tBiQuad* const ft, float input) | |
| 720 | +{ | |
| 721 | + _tBiQuad* f = *ft; | |
| 722 | + | |
| 723 | + float in = input * f->gain; | |
| 724 | + float out = f->b0 * in + f->b1 * f->lastIn[0] + f->b2 * f->lastIn[1]; | |
| 725 | + out -= f->a2 * f->lastOut[1] + f->a1 * f->lastOut[0]; | |
| 726 | + | |
| 727 | + f->lastIn[1] = f->lastIn[0]; | |
| 728 | + f->lastIn[0] = in; | |
| 729 | + | |
| 730 | + f->lastOut[1] = f->lastOut[0]; | |
| 731 | + f->lastOut[0] = out; | |
| 732 | + | |
| 733 | + return out; | |
| 734 | +} | |
| 735 | + | |
| 736 | +void tBiQuad_setResonance(tBiQuad* const ft, float freq, float radius, oBool normalize) | |
| 737 | +{ | |
| 738 | + _tBiQuad* f = *ft; | |
| 739 | + | |
| 740 | + if (freq < 0.0f) freq = 0.0f; | |
| 741 | + if (freq > (leaf.sampleRate * 0.49f)) freq = leaf.sampleRate * 0.49f; | |
| 742 | + if (radius < 0.0f) radius = 0.0f; | |
| 743 | + if (radius >= 1.0f) radius = 1.0f; | |
| 744 | + | |
| 745 | + f->frequency = freq; | |
| 746 | + f->radius = radius; | |
| 747 | + f->normalize = normalize; | |
| 748 | + | |
| 749 | + f->a2 = radius * radius; | |
| 750 | + f->a1 = -2.0f * radius * cosf(freq * leaf.twoPiTimesInvSampleRate); | |
| 751 | + | |
| 752 | + if (normalize) | |
| 753 | + { | |
| 754 | + f->b0 = 0.5f - 0.5f * f->a2; | |
| 755 | + f->b1 = 0.0f; | |
| 756 | + f->b2 = -f->b0; | |
| 757 | + } | |
| 758 | +} | |
| 759 | + | |
| 760 | +void tBiQuad_setNotch(tBiQuad* const ft, float freq, float radius) | |
| 761 | +{ | |
| 762 | + _tBiQuad* f = *ft; | |
| 763 | + | |
| 764 | + if (freq < 0.0f) freq = 0.0f; | |
| 765 | + if (freq > (leaf.sampleRate * 0.49f)) freq = leaf.sampleRate * 0.49f; | |
| 766 | + if (radius < 0.0f) radius = 0.0f; | |
| 767 | + | |
| 768 | + f->b2 = radius * radius; | |
| 769 | + f->b1 = -2.0f * radius * cosf(freq * leaf.twoPiTimesInvSampleRate); // OPTIMIZE with LOOKUP or APPROXIMATION | |
| 770 | + | |
| 771 | + // Does not attempt to normalize filter gain. | |
| 772 | +} | |
| 773 | + | |
| 774 | +void tBiQuad_setEqualGainZeros(tBiQuad* const ft) | |
| 775 | +{ | |
| 776 | + _tBiQuad* f = *ft; | |
| 777 | + f->b0 = 1.0f; | |
| 778 | + f->b1 = 0.0f; | |
| 779 | + f->b2 = -1.0f; | |
| 780 | +} | |
| 781 | + | |
| 782 | +void tBiQuad_setB0(tBiQuad* const ft, float b0) | |
| 783 | +{ | |
| 784 | + _tBiQuad* f = *ft; | |
| 785 | + f->b0 = b0; | |
| 786 | +} | |
| 787 | + | |
| 788 | +void tBiQuad_setB1(tBiQuad* const ft, float b1) | |
| 789 | +{ | |
| 790 | + _tBiQuad* f = *ft; | |
| 791 | + f->b1 = b1; | |
| 792 | +} | |
| 793 | + | |
| 794 | +void tBiQuad_setB2(tBiQuad* const ft, float b2) | |
| 795 | +{ | |
| 796 | + _tBiQuad* f = *ft; | |
| 797 | + f->b2 = b2; | |
| 798 | +} | |
| 799 | + | |
| 800 | +void tBiQuad_setA1(tBiQuad* const ft, float a1) | |
| 801 | +{ | |
| 802 | + _tBiQuad* f = *ft; | |
| 803 | + f->a1 = a1; | |
| 804 | +} | |
| 805 | + | |
| 806 | +void tBiQuad_setA2(tBiQuad* const ft, float a2) | |
| 807 | +{ | |
| 808 | + _tBiQuad* f = *ft; | |
| 809 | + f->a2 = a2; | |
| 810 | +} | |
| 811 | + | |
| 812 | +void tBiQuad_setCoefficients(tBiQuad* const ft, float b0, float b1, float b2, float a1, float a2) | |
| 813 | +{ | |
| 814 | + _tBiQuad* f = *ft; | |
| 815 | + f->b0 = b0; | |
| 816 | + f->b1 = b1; | |
| 817 | + f->b2 = b2; | |
| 818 | + f->a1 = a1; | |
| 819 | + f->a2 = a2; | |
| 820 | +} | |
| 821 | + | |
| 822 | +void tBiQuad_setGain(tBiQuad* const ft, float gain) | |
| 823 | +{ | |
| 824 | + _tBiQuad* f = *ft; | |
| 825 | + f->gain = gain; | |
| 826 | +} | |
| 827 | + | |
| 828 | +void tBiQuadSampleRateChanged(tBiQuad* const ft) | |
| 829 | +{ | |
| 830 | + _tBiQuad* f = *ft; | |
| 831 | + f->a2 = f->radius * f->radius; | |
| 832 | + f->a1 = -2.0f * f->radius * cosf(f->frequency * leaf.twoPiTimesInvSampleRate); | |
| 833 | + | |
| 834 | + if (f->normalize) | |
| 835 | + { | |
| 836 | + f->b0 = 0.5f - 0.5f * f->a2; | |
| 837 | + f->b1 = 0.0f; | |
| 838 | + f->b2 = -f->b0; | |
| 839 | + } | |
| 840 | +} | |
| 841 | + | |
| 842 | +// Less efficient, more accurate version of SVF, in which cutoff frequency is taken as floating point Hz value and tanf | |
| 843 | +// is calculated when frequency changes. | |
| 844 | +void tSVF_init(tSVF* const svff, SVFType type, float freq, float Q) | |
| 845 | +{ | |
| 846 | + tSVF_initToPool (svff, type, freq, Q, &leaf.mempool); | |
| 847 | + | |
| 848 | + // or maybe this? | |
| 849 | + /* | |
| 850 | + * hp=1 bp=A/Q (where A is 10^(G/40) and G is gain in decibels) and lp = 1 | |
| 851 | + */ | |
| 852 | + | |
| 853 | +} | |
| 854 | + | |
| 855 | +void tSVF_free(tSVF* const svff) | |
| 856 | +{ | |
| 857 | + tSVF_freeFromPool (svff, &leaf.mempool); | |
| 858 | +} | |
| 859 | + | |
| 860 | +void tSVF_initToPool (tSVF* const svff, SVFType type, float freq, float Q, tMempool* const mp) | |
| 861 | +{ | |
| 862 | + _tMempool* m = *mp; | |
| 863 | + _tSVF* svf = *svff = (_tSVF*) mpool_alloc(sizeof(_tSVF), m); | |
| 864 | + | |
| 865 | + svf->type = type; | |
| 866 | + | |
| 867 | + svf->ic1eq = 0; | |
| 868 | + svf->ic2eq = 0; | |
| 869 | + svf->Q = Q; | |
| 870 | + svf->cutoff = freq; | |
| 871 | + svf->g = tanf(PI * freq * leaf.invSampleRate); | |
| 872 | + svf->k = 1.0f/Q; | |
| 873 | + svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k)); | |
| 874 | + svf->a2 = svf->g*svf->a1; | |
| 875 | + svf->a3 = svf->g*svf->a2; | |
| 876 | + svf->cH = 0.0f; | |
| 877 | + svf->cB = 0.0f; | |
| 878 | + svf->cL = 1.0f; | |
| 879 | + | |
| 880 | + if (type == SVFTypeLowpass) | |
| 881 | + { | |
| 882 | + svf->cH = 0.0f; | |
| 883 | + svf->cB = 0.0f; | |
| 884 | + svf->cBK = 0.0f; | |
| 885 | + svf->cL = 1.0f; | |
| 886 | + } | |
| 887 | + else if (type == SVFTypeBandpass) | |
| 888 | + { | |
| 889 | + svf->cH = 0.0f; | |
| 890 | + svf->cB = 1.0f; | |
| 891 | + svf->cBK = 0.0f; | |
| 892 | + svf->cL = 0.0f; | |
| 893 | + } | |
| 894 | + | |
| 895 | + else if (type == SVFTypeHighpass) | |
| 896 | + { | |
| 897 | + svf->cH = 1.0f; | |
| 898 | + svf->cB = 0.0f; | |
| 899 | + svf->cBK = -1.0f; | |
| 900 | + svf->cL = -1.0f; | |
| 901 | + } | |
| 902 | + | |
| 903 | + else if (type == SVFTypeNotch) | |
| 904 | + { | |
| 905 | + svf->cH = 1.0f; | |
| 906 | + svf->cB = 0.0f; | |
| 907 | + svf->cBK = -1.0f; | |
| 908 | + svf->cL = 0.0f; | |
| 909 | + } | |
| 910 | + | |
| 911 | + | |
| 912 | + else if (type == SVFTypePeak) | |
| 913 | + { | |
| 914 | + svf->cH = 1.0f; | |
| 915 | + svf->cB = 0.0f; | |
| 916 | + svf->cBK = -1.0f; | |
| 917 | + svf->cL = -2.0f; | |
| 918 | + } | |
| 919 | +} | |
| 920 | + | |
| 921 | +void tSVF_freeFromPool (tSVF* const svff, tMempool* const mp) | |
| 922 | +{ | |
| 923 | + _tMempool* m = *mp; | |
| 924 | + _tSVF* svf = *svff; | |
| 925 | + | |
| 926 | + mpool_free((char*)svf, m); | |
| 927 | +} | |
| 928 | + | |
| 929 | +float tSVF_tick(tSVF* const svff, float v0) | |
| 930 | +{ | |
| 931 | + _tSVF* svf = *svff; | |
| 932 | + | |
| 933 | + float v1,v2,v3; | |
| 934 | + v3 = v0 - svf->ic2eq; | |
| 935 | + v1 = (svf->a1 * svf->ic1eq) + (svf->a2 * v3); | |
| 936 | + v2 = svf->ic2eq + (svf->a2 * svf->ic1eq) + (svf->a3 * v3); | |
| 937 | + svf->ic1eq = (2.0f * v1) - svf->ic1eq; | |
| 938 | + svf->ic2eq = (2.0f * v2) - svf->ic2eq; | |
| 939 | + | |
| 940 | + return (v0 * svf->cH) + (v1 * svf->cB) + (svf->k * v1 * svf->cBK) + (v2 * svf->cL); | |
| 941 | +} | |
| 942 | + | |
| 943 | +void tSVF_setFreq(tSVF* const svff, float freq) | |
| 944 | +{ | |
| 945 | + _tSVF* svf = *svff; | |
| 946 | + svf->cutoff = freq; | |
| 947 | + svf->g = tanf(PI * freq * leaf.invSampleRate); | |
| 948 | + svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k)); | |
| 949 | + svf->a2 = svf->g * svf->a1; | |
| 950 | + svf->a3 = svf->g * svf->a2; | |
| 951 | +} | |
| 952 | + | |
| 953 | +void tSVF_setQ(tSVF* const svff, float Q) | |
| 954 | +{ | |
| 955 | + _tSVF* svf = *svff; | |
| 956 | + svf->Q = Q; | |
| 957 | + svf->k = 1.0f/Q; | |
| 958 | + | |
| 959 | + svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k)); | |
| 960 | + svf->a2 = svf->g * svf->a1; | |
| 961 | + svf->a3 = svf->g * svf->a2; | |
| 962 | +} | |
| 963 | + | |
| 964 | +void tSVF_setFreqAndQ(tSVF* const svff, float freq, float Q) | |
| 965 | +{ | |
| 966 | + _tSVF* svf = *svff; | |
| 967 | + svf->k = 1.0f/Q; | |
| 968 | + svf->g = tanf(PI * freq * leaf.invSampleRate); | |
| 969 | + svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k)); | |
| 970 | + svf->a2 = svf->g * svf->a1; | |
| 971 | + svf->a3 = svf->g * svf->a2; | |
| 972 | +} | |
| 973 | + | |
| 974 | +// Efficient version of tSVF where frequency is set based on 12-bit integer input for lookup in tanh wavetable. | |
| 975 | +void tEfficientSVF_init(tEfficientSVF* const svff, SVFType type, uint16_t input, float Q) | |
| 976 | +{ | |
| 977 | + _tEfficientSVF* svf = *svff = (_tEfficientSVF*) leaf_alloc(sizeof(_tEfficientSVF)); | |
| 978 | + | |
| 979 | + svf->type = type; | |
| 980 | + | |
| 981 | + svf->ic1eq = 0; | |
| 982 | + svf->ic2eq = 0; | |
| 983 | + | |
| 984 | + svf->g = __leaf_table_filtertan[input]; | |
| 985 | + svf->k = 1.0f/Q; | |
| 986 | + svf->a1 = 1.0f/(1.0f+svf->g*(svf->g+svf->k)); | |
| 987 | + svf->a2 = svf->g*svf->a1; | |
| 988 | + svf->a3 = svf->g*svf->a2; | |
| 989 | +} | |
| 990 | + | |
| 991 | +void tEfficientSVF_free(tEfficientSVF* const svff) | |
| 992 | +{ | |
| 993 | + _tEfficientSVF* svf = *svff; | |
| 994 | + | |
| 995 | + leaf_free((char*)svf); | |
| 996 | +} | |
| 997 | + | |
| 998 | +void tEfficientSVF_initToPool (tEfficientSVF* const svff, SVFType type, uint16_t input, float Q, tMempool* const mp) | |
| 999 | +{ | |
| 1000 | + _tMempool* m = *mp; | |
| 1001 | + _tEfficientSVF* svf = *svff = (_tEfficientSVF*) mpool_alloc(sizeof(_tEfficientSVF), m); | |
| 1002 | + | |
| 1003 | + svf->type = type; | |
| 1004 | + | |
| 1005 | + svf->ic1eq = 0; | |
| 1006 | + svf->ic2eq = 0; | |
| 1007 | + | |
| 1008 | + svf->g = __leaf_table_filtertan[input]; | |
| 1009 | + svf->k = 1.0f/Q; | |
| 1010 | + svf->a1 = 1.0f/(1.0f+svf->g*(svf->g+svf->k)); | |
| 1011 | + svf->a2 = svf->g*svf->a1; | |
| 1012 | + svf->a3 = svf->g*svf->a2; | |
| 1013 | +} | |
| 1014 | + | |
| 1015 | +void tEfficientSVF_freeFromPool (tEfficientSVF* const svff, tMempool* const mp) | |
| 1016 | +{ | |
| 1017 | + _tMempool* m = *mp; | |
| 1018 | + _tEfficientSVF* svf = *svff; | |
| 1019 | + | |
| 1020 | + mpool_free((char*)svf, m); | |
| 1021 | +} | |
| 1022 | + | |
| 1023 | +float tEfficientSVF_tick(tEfficientSVF* const svff, float v0) | |
| 1024 | +{ | |
| 1025 | + _tEfficientSVF* svf = *svff; | |
| 1026 | + | |
| 1027 | + float v1,v2,v3; | |
| 1028 | + v3 = v0 - svf->ic2eq; | |
| 1029 | + v1 = (svf->a1 * svf->ic1eq) + (svf->a2 * v3); | |
| 1030 | + v2 = svf->ic2eq + (svf->a2 * svf->ic1eq) + (svf->a3 * v3); | |
| 1031 | + svf->ic1eq = (2.0f * v1) - svf->ic1eq; | |
| 1032 | + svf->ic2eq = (2.0f * v2) - svf->ic2eq; | |
| 1033 | + | |
| 1034 | + if (svf->type == SVFTypeLowpass) return v2; | |
| 1035 | + else if (svf->type == SVFTypeBandpass) return v1; | |
| 1036 | + else if (svf->type == SVFTypeHighpass) return v0 - (svf->k * v1) - v2; | |
| 1037 | + else if (svf->type == SVFTypeNotch) return v0 - (svf->k * v1); | |
| 1038 | + else if (svf->type == SVFTypePeak) return v0 - (svf->k * v1) - (2.0f * v2); | |
| 1039 | + else return 0.0f; | |
| 1040 | + | |
| 1041 | +} | |
| 1042 | + | |
| 1043 | +void tEfficientSVF_setFreq(tEfficientSVF* const svff, uint16_t input) | |
| 1044 | +{ | |
| 1045 | + _tEfficientSVF* svf = *svff; | |
| 1046 | + | |
| 1047 | + svf->g = __leaf_table_filtertan[input]; | |
| 1048 | + svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k)); | |
| 1049 | + svf->a2 = svf->g * svf->a1; | |
| 1050 | + svf->a3 = svf->g * svf->a2; | |
| 1051 | +} | |
| 1052 | + | |
| 1053 | +void tEfficientSVF_setQ(tEfficientSVF* const svff, float Q) | |
| 1054 | +{ | |
| 1055 | + _tEfficientSVF* svf = *svff; | |
| 1056 | + | |
| 1057 | + svf->k = 1.0f/Q; | |
| 1058 | + svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k)); | |
| 1059 | + svf->a2 = svf->g * svf->a1; | |
| 1060 | + svf->a3 = svf->g * svf->a2; | |
| 1061 | +} | |
| 1062 | + | |
| 1063 | +/* Highpass */ | |
| 1064 | +void tHighpass_init(tHighpass* const ft, float freq) | |
| 1065 | +{ | |
| 1066 | + _tHighpass* f = *ft = (_tHighpass*) leaf_alloc(sizeof(_tHighpass)); | |
| 1067 | + | |
| 1068 | + f->R = (1.0f - (freq * leaf.twoPiTimesInvSampleRate)); | |
| 1069 | + f->ys = 0.0f; | |
| 1070 | + f->xs = 0.0f; | |
| 1071 | + | |
| 1072 | + f->frequency = freq; | |
| 1073 | +} | |
| 1074 | + | |
| 1075 | +void tHighpass_free(tHighpass* const ft) | |
| 1076 | +{ | |
| 1077 | + _tHighpass* f = *ft; | |
| 1078 | + | |
| 1079 | + leaf_free((char*)f); | |
| 1080 | +} | |
| 1081 | + | |
| 1082 | +void tHighpass_initToPool (tHighpass* const ft, float freq, tMempool* const mp) | |
| 1083 | +{ | |
| 1084 | + _tMempool* m = *mp; | |
| 1085 | + _tHighpass* f = *ft = (_tHighpass*) mpool_calloc(sizeof(_tHighpass), m); | |
| 1086 | + | |
| 1087 | + f->R = (1.0f - (freq * leaf.twoPiTimesInvSampleRate)); | |
| 1088 | + f->ys = 0.0f; | |
| 1089 | + f->xs = 0.0f; | |
| 1090 | + | |
| 1091 | + f->frequency = freq; | |
| 1092 | +} | |
| 1093 | + | |
| 1094 | +void tHighpass_freeFromPool (tHighpass* const ft, tMempool* const mp) | |
| 1095 | +{ | |
| 1096 | + _tMempool* m = *mp; | |
| 1097 | + _tHighpass* f = *ft; | |
| 1098 | + | |
| 1099 | + mpool_free((char*)f, m); | |
| 1100 | +} | |
| 1101 | + | |
| 1102 | +void tHighpass_setFreq(tHighpass* const ft, float freq) | |
| 1103 | +{ | |
| 1104 | + _tHighpass* f = *ft; | |
| 1105 | + f->frequency = freq; | |
| 1106 | + f->R = (1.0f - (freq * leaf.twoPiTimesInvSampleRate)); | |
| 1107 | + | |
| 1108 | +} | |
| 1109 | + | |
| 1110 | +float tHighpass_getFreq(tHighpass* const ft) | |
| 1111 | +{ | |
| 1112 | + _tHighpass* f = *ft; | |
| 1113 | + return f->frequency; | |
| 1114 | +} | |
| 1115 | + | |
| 1116 | +// From JOS DC Blocker | |
| 1117 | +float tHighpass_tick(tHighpass* const ft, float x) | |
| 1118 | +{ | |
| 1119 | + _tHighpass* f = *ft; | |
| 1120 | + f->ys = x - f->xs + f->R * f->ys; | |
| 1121 | + f->xs = x; | |
| 1122 | + return f->ys; | |
| 1123 | +} | |
| 1124 | + | |
| 1125 | +void tHighpassSampleRateChanged(tHighpass* const ft) | |
| 1126 | +{ | |
| 1127 | + _tHighpass* f = *ft; | |
| 1128 | + f->R = (1.0f-((f->frequency * 2.0f * 3.14f) * leaf.invSampleRate)); | |
| 1129 | +} | |
| 1130 | + | |
| 1131 | +void tButterworth_init(tButterworth* const ft, int N, float f1, float f2) | |
| 1132 | +{ | |
| 1133 | + _tButterworth* f = *ft = (_tButterworth*) leaf_alloc(sizeof(_tButterworth)); | |
| 1134 | + | |
| 1135 | + f->f1 = f1; | |
| 1136 | + f->f2 = f2; | |
| 1137 | + f->gain = 1.0f; | |
| 1138 | + | |
| 1139 | + f->N = N; | |
| 1140 | + | |
| 1141 | + if (f->N > NUM_SVF_BW) f->N = NUM_SVF_BW; | |
| 1142 | + | |
| 1143 | + for(int i = 0; i < N/2; ++i) | |
| 1144 | + { | |
| 1145 | + tSVF_init(&f->low[i], SVFTypeHighpass, f1, 0.5f/cosf((1.0f+2.0f*i)*PI/(2*N))); | |
| 1146 | + tSVF_init(&f->high[i], SVFTypeLowpass, f2, 0.5f/cosf((1.0f+2.0f*i)*PI/(2*N))); | |
| 1147 | + } | |
| 1148 | +} | |
| 1149 | + | |
| 1150 | +void tButterworth_free(tButterworth* const ft) | |
| 1151 | +{ | |
| 1152 | + _tButterworth* f = *ft; | |
| 1153 | + | |
| 1154 | + for(int i = 0; i < f->N/2; ++i) | |
| 1155 | + { | |
| 1156 | + tSVF_free(&f->low[i]); | |
| 1157 | + tSVF_free(&f->high[i]); | |
| 1158 | + } | |
| 1159 | + | |
| 1160 | + leaf_free((char*)f); | |
| 1161 | +} | |
| 1162 | + | |
| 1163 | +void tButterworth_initToPool (tButterworth* const ft, int N, float f1, float f2, tMempool* const mp) | |
| 1164 | +{ | |
| 1165 | + _tMempool* m = *mp; | |
| 1166 | + _tButterworth* f = *ft = (_tButterworth*) mpool_alloc(sizeof(_tButterworth), m); | |
| 1167 | + | |
| 1168 | + f->f1 = f1; | |
| 1169 | + f->f2 = f2; | |
| 1170 | + f->gain = 1.0f; | |
| 1171 | + | |
| 1172 | + f->N = N; | |
| 1173 | + | |
| 1174 | + if (f->N > NUM_SVF_BW) f->N = NUM_SVF_BW; | |
| 1175 | + | |
| 1176 | + for(int i = 0; i < N/2; ++i) | |
| 1177 | + { | |
| 1178 | + tSVF_initToPool(&f->low[i], SVFTypeHighpass, f1, 0.5f/cosf((1.0f+2.0f*i)*PI/(2*N)), mp); | |
| 1179 | + tSVF_initToPool(&f->high[i], SVFTypeLowpass, f2, 0.5f/cosf((1.0f+2.0f*i)*PI/(2*N)), mp); | |
| 1180 | + } | |
| 1181 | +} | |
| 1182 | + | |
| 1183 | +void tButterworth_freeFromPool (tButterworth* const ft, tMempool* const mp) | |
| 1184 | +{ | |
| 1185 | + _tMempool* m = *mp; | |
| 1186 | + _tButterworth* f = *ft; | |
| 1187 | + | |
| 1188 | + for(int i = 0; i < f->N/2; ++i) | |
| 1189 | + { | |
| 1190 | + tSVF_freeFromPool(&f->low[i], mp); | |
| 1191 | + tSVF_freeFromPool(&f->high[i], mp); | |
| 1192 | + } | |
| 1193 | + | |
| 1194 | + mpool_free((char*)f, m); | |
| 1195 | +} | |
| 1196 | + | |
| 1197 | +float tButterworth_tick(tButterworth* const ft, float samp) | |
| 1198 | +{ | |
| 1199 | + _tButterworth* f = *ft; | |
| 1200 | + | |
| 1201 | + for(int i = 0; i < ((f->N)/2); ++i) | |
| 1202 | + { | |
| 1203 | + samp = tSVF_tick(&f->low[i],samp); | |
| 1204 | + samp = tSVF_tick(&f->high[i],samp); | |
| 1205 | + } | |
| 1206 | + return samp; | |
| 1207 | +} | |
| 1208 | + | |
| 1209 | +void tButterworth_setF1(tButterworth* const ft, float f1) | |
| 1210 | +{ | |
| 1211 | + _tButterworth* f = *ft; | |
| 1212 | + | |
| 1213 | + f->f1 = f1; | |
| 1214 | + for(int i = 0; i < ((f->N)/2); ++i) tSVF_setFreq(&f->low[i], f1); | |
| 1215 | +} | |
| 1216 | + | |
| 1217 | +void tButterworth_setF2(tButterworth* const ft, float f2) | |
| 1218 | +{ | |
| 1219 | + _tButterworth* f = *ft; | |
| 1220 | + | |
| 1221 | + f->f2 = f2; | |
| 1222 | + for(int i = 0; i < ((f->N)/2); ++i) tSVF_setFreq(&f->high[i], f2); | |
| 1223 | +} | |
| 1224 | + | |
| 1225 | +void tButterworth_setFreqs(tButterworth* const ft, float f1, float f2) | |
| 1226 | +{ | |
| 1227 | + _tButterworth* f = *ft; | |
| 1228 | + | |
| 1229 | + f->f1 = f1; | |
| 1230 | + f->f2 = f2; | |
| 1231 | + for(int i = 0; i < ((f->N)/2); ++i) | |
| 1232 | + { | |
| 1233 | + tSVF_setFreq(&f->low[i], f1); | |
| 1234 | + tSVF_setFreq(&f->high[i], f2); | |
| 1235 | + } | |
| 1236 | +} | |
| 1237 | + | |
| 1238 | +void tFIR_init(tFIR* const firf, float* coeffs, int numTaps) | |
| 1239 | +{ | |
| 1240 | + _tFIR* fir = *firf = (_tFIR*) leaf_alloc(sizeof(_tFIR)); | |
| 1241 | + | |
| 1242 | + fir->numTaps = numTaps; | |
| 1243 | + fir->coeff = coeffs; | |
| 1244 | + fir->past = (float*)leaf_alloc(sizeof(float) * fir->numTaps); | |
| 1245 | + for (int i = 0; i < fir->numTaps; ++i) fir->past[i] = 0.0f; | |
| 1246 | +} | |
| 1247 | + | |
| 1248 | +void tFIR_free(tFIR* const firf) | |
| 1249 | +{ | |
| 1250 | + _tFIR* fir = *firf; | |
| 1251 | + | |
| 1252 | + leaf_free((char*)fir->past); | |
| 1253 | + leaf_free((char*)fir); | |
| 1254 | +} | |
| 1255 | + | |
| 1256 | +void tFIR_initToPool (tFIR* const firf, float* coeffs, int numTaps, tMempool* const mp) | |
| 1257 | +{ | |
| 1258 | + _tMempool* m = *mp; | |
| 1259 | + _tFIR* fir = *firf = (_tFIR*) mpool_alloc(sizeof(_tFIR), m); | |
| 1260 | + | |
| 1261 | + fir->numTaps = numTaps; | |
| 1262 | + fir->coeff = coeffs; | |
| 1263 | + fir->past = (float*) mpool_alloc(sizeof(float) * fir->numTaps, m); | |
| 1264 | + for (int i = 0; i < fir->numTaps; ++i) fir->past[i] = 0.0f; | |
| 1265 | +} | |
| 1266 | + | |
| 1267 | +void tFIR_freeFromPool (tFIR* const firf, tMempool* const mp) | |
| 1268 | +{ | |
| 1269 | + _tMempool* m = *mp; | |
| 1270 | + _tFIR* fir = *firf; | |
| 1271 | + | |
| 1272 | + mpool_free((char*)fir->past, m); | |
| 1273 | + mpool_free((char*)fir, m); | |
| 1274 | +} | |
| 1275 | + | |
| 1276 | +float tFIR_tick(tFIR* const firf, float input) | |
| 1277 | +{ | |
| 1278 | + _tFIR* fir = *firf; | |
| 1279 | + | |
| 1280 | + fir->past[0] = input; | |
| 1281 | + float y = 0.0f; | |
| 1282 | + for (int i = 0; i < fir->numTaps; ++i) y += fir->past[i]*fir->coeff[i]; | |
| 1283 | + for (int i = fir->numTaps-1; i > 0; --i) fir->past[i] = fir->past[i-1]; | |
| 1284 | + return y; | |
| 1285 | +} | |
| 1286 | + | |
| 1287 | +//--------------------------------------------- | |
| 1288 | +//// | |
| 1289 | +/// Median filter implemented based on James McCartney's median filter in Supercollider, | |
| 1290 | +/// translated from a Gen~ port of the Supercollider code that I believe was made by Rodrigo Costanzo and which I got from PA Tremblay - JS | |
| 1291 | + | |
| 1292 | + | |
| 1293 | +void tMedianFilter_init (tMedianFilter* const f, int size) | |
| 1294 | +{ | |
| 1295 | + tMedianFilter_initToPool(f, size, &leaf.mempool); | |
| 1296 | +} | |
| 1297 | +void tMedianFilter_free (tMedianFilter* const f) | |
| 1298 | +{ | |
| 1299 | + tMedianFilter_freeFromPool(f, &leaf.mempool); | |
| 1300 | +} | |
| 1301 | +void tMedianFilter_initToPool (tMedianFilter* const mf, int size, tMempool* const mp) | |
| 1302 | +{ | |
| 1303 | + _tMempool* m = *mp; | |
| 1304 | + _tMedianFilter* f = *mf = (_tMedianFilter*) mpool_alloc(sizeof(_tMedianFilter), m); | |
| 1305 | + | |
| 1306 | + f->size = size; | |
| 1307 | + f->middlePosition = size / 2; | |
| 1308 | + f->last = size - 1; | |
| 1309 | + f->pos = -1; | |
| 1310 | + f->val = (float*) mpool_alloc(sizeof(float) * size, m); | |
| 1311 | + f->age = (int*) mpool_alloc(sizeof(int) * size, m); | |
| 1312 | + for (int i = 0; i < f->size; ++i) | |
| 1313 | + { | |
| 1314 | + f->val[i] = 0.0f; | |
| 1315 | + f->age[i] = i; | |
| 1316 | + } | |
| 1317 | + | |
| 1318 | +} | |
| 1319 | +void tMedianFilter_freeFromPool (tMedianFilter* const mf, tMempool* const mp) | |
| 1320 | +{ | |
| 1321 | + _tMempool* m = *mp; | |
| 1322 | + _tMedianFilter* f = *mf; | |
| 1323 | + | |
| 1324 | + mpool_free((char*)f->val, m); | |
| 1325 | + mpool_free((char*)f->age, m); | |
| 1326 | + mpool_free((char*)f, m); | |
| 1327 | +} | |
| 1328 | + | |
| 1329 | +float tMedianFilter_tick (tMedianFilter* const mf, float input) | |
| 1330 | +{ | |
| 1331 | + _tMedianFilter* f = *mf; | |
| 1332 | + | |
| 1333 | + for(int i=0; i<f->size; i++) { | |
| 1334 | + int thisAge = f->age[i]; | |
| 1335 | + if(thisAge == f->last) { | |
| 1336 | + f->pos = i; | |
| 1337 | + } | |
| 1338 | + else { | |
| 1339 | + thisAge++; | |
| 1340 | + f->age[i] = thisAge; | |
| 1341 | + } | |
| 1342 | + } | |
| 1343 | + | |
| 1344 | + while( f->pos!=0 ) { | |
| 1345 | + float test = f->val[f->pos-1]; | |
| 1346 | + if(input < test) { | |
| 1347 | + f->val[f->pos]=test; | |
| 1348 | + f->age[f->pos]=f->age[f->pos-1]; | |
| 1349 | + f->pos -= 1; | |
| 1350 | + } else {break;} | |
| 1351 | + } | |
| 1352 | + | |
| 1353 | + while(f->pos != f->last) { | |
| 1354 | + float test = f->val[f->pos+1]; | |
| 1355 | + if( input > test) { | |
| 1356 | + f->val[f->pos] = test; | |
| 1357 | + f->age[f->pos] = f->age[f->pos+1]; | |
| 1358 | + f->pos += 1; | |
| 1359 | + } else {break;} | |
| 1360 | + } | |
| 1361 | + | |
| 1362 | + f->val[f->pos] = input; | |
| 1363 | + f->age[f->pos] = 0; | |
| 1364 | + | |
| 1365 | + return f->val[f->middlePosition]; | |
| 1366 | +} | |
| 1367 | + | |
| 1368 | +///// | |
| 1369 | + | |
| 1370 | +void tVZFilter_init (tVZFilter* const vf, VZFilterType type, float freq, float bandWidth) | |
| 1371 | +{ | |
| 1372 | + tVZFilter_initToPool(vf, type, freq, bandWidth, &leaf.mempool); | |
| 1373 | +} | |
| 1374 | + | |
| 1375 | +void tVZFilter_free (tVZFilter* const vf) | |
| 1376 | +{ | |
| 1377 | + tVZFilter_freeFromPool(vf, &leaf.mempool); | |
| 1378 | +} | |
| 1379 | +void tVZFilter_initToPool (tVZFilter* const vf, VZFilterType type, float freq, float bandWidth, tMempool* const mp) | |
| 1380 | +{ | |
| 1381 | + | |
| 1382 | + _tMempool* m = *mp; | |
| 1383 | + _tVZFilter* f = *vf = (_tVZFilter*) mpool_alloc(sizeof(_tVZFilter), m); | |
| 1384 | + f->fc = freq; | |
| 1385 | + f->type = type; | |
| 1386 | + f->G = ONE_OVER_SQRT2; | |
| 1387 | + f->invG = 1.0f/ONE_OVER_SQRT2; | |
| 1388 | + f->B = bandWidth; | |
| 1389 | + f->m = 0.0f; | |
| 1390 | + f->s1 = 0.0f; | |
| 1391 | + f->s2 = 0.0f; | |
| 1392 | + f->sr = leaf.sampleRate; | |
| 1393 | + f->inv_sr = leaf.invSampleRate; | |
| 1394 | + tVZFilter_calcCoeffs(vf); | |
| 1395 | + | |
| 1396 | + | |
| 1397 | +} | |
| 1398 | +void tVZFilter_freeFromPool (tVZFilter* const vf, tMempool* const mp) | |
| 1399 | +{ | |
| 1400 | + _tMempool* m = *mp; | |
| 1401 | + _tVZFilter* f = *vf = (_tVZFilter*) mpool_alloc(sizeof(_tVZFilter), m); | |
| 1402 | + mpool_free((char*)f, m); | |
| 1403 | +} | |
| 1404 | + | |
| 1405 | +void tVZFilter_setSampleRate (tVZFilter* const vf, float sampleRate) | |
| 1406 | +{ | |
| 1407 | + _tVZFilter* f = *vf; | |
| 1408 | + f->sr = sampleRate; | |
| 1409 | + f->inv_sr = 1.0f/sampleRate; | |
| 1410 | +} | |
| 1411 | + | |
| 1412 | +float tVZFilter_tick (tVZFilter* const vf, float in) | |
| 1413 | +{ | |
| 1414 | + _tVZFilter* f = *vf; | |
| 1415 | + | |
| 1416 | + float yL, yB, yH; | |
| 1417 | + | |
| 1418 | + // compute highpass output via Eq. 5.1: | |
| 1419 | + yH = (in - f->R2*f->s1 - f->g*f->s1 - f->s2) * f->h; | |
| 1420 | + | |
| 1421 | + // compute bandpass output by applying 1st integrator to highpass output: | |
| 1422 | + yB = tanhf(f->g*yH) + f->s1; | |
| 1423 | + f->s1 = f->g*yH + yB; // state update in 1st integrator | |
| 1424 | + | |
| 1425 | + // compute lowpass output by applying 2nd integrator to bandpass output: | |
| 1426 | + yL = tanhf(f->g*yB) + f->s2; | |
| 1427 | + f->s2 = f->g*yB + yL; // state update in 2nd integrator | |
| 1428 | + | |
| 1429 | + //according to the Vadim paper, we could add saturation to this model by adding a tanh in the integration stage. | |
| 1430 | + // | |
| 1431 | + //seems like that might look like this: | |
| 1432 | + // y = tanh(g*x) + s; // output computation | |
| 1433 | + // s = g*x + y; // state update | |
| 1434 | + | |
| 1435 | + //instead of this: | |
| 1436 | + // y = g*x + s; // output computation | |
| 1437 | + // s = g*x + y; // state update | |
| 1438 | + | |
| 1439 | + return f->cL*yL + f->cB*yB + f->cH*yH; | |
| 1440 | + | |
| 1441 | +} | |
| 1442 | + | |
| 1443 | +float tVZFilter_tickEfficient (tVZFilter* const vf, float in) | |
| 1444 | +{ | |
| 1445 | + _tVZFilter* f = *vf; | |
| 1446 | + | |
| 1447 | + float yL, yB, yH; | |
| 1448 | + | |
| 1449 | + // compute highpass output via Eq. 5.1: | |
| 1450 | + yH = (in - f->R2*f->s1 - f->g*f->s1 - f->s2) * f->h; | |
| 1451 | + | |
| 1452 | + // compute bandpass output by applying 1st integrator to highpass output: | |
| 1453 | + yB = (f->g*yH) + f->s1; | |
| 1454 | + f->s1 = f->g*yH + yB; // state update in 1st integrator | |
| 1455 | + | |
| 1456 | + // compute lowpass output by applying 2nd integrator to bandpass output: | |
| 1457 | + yL = (f->g*yB) + f->s2; | |
| 1458 | + f->s2 = f->g*yB + yL; // state update in 2nd integrator | |
| 1459 | + | |
| 1460 | + //according to the Vadim paper, we could add saturation to this model by adding a tanh in the integration stage. | |
| 1461 | + // | |
| 1462 | + //seems like that might look like this: | |
| 1463 | + // y = tanh(g*x) + s; // output computation | |
| 1464 | + // s = g*x + y; // state update | |
| 1465 | + | |
| 1466 | + //instead of this: | |
| 1467 | + // y = g*x + s; // output computation | |
| 1468 | + // s = g*x + y; // state update | |
| 1469 | + | |
| 1470 | + return f->cL*yL + f->cB*yB + f->cH*yH; | |
| 1471 | + | |
| 1472 | +} | |
| 1473 | + | |
| 1474 | +float tVZFilter_tickEfficientBP (tVZFilter* const vf, float in) | |
| 1475 | +{ | |
| 1476 | + _tVZFilter* f = *vf; | |
| 1477 | + | |
| 1478 | + float yL, yB, yH; | |
| 1479 | + | |
| 1480 | + // compute highpass output via Eq. 5.1: | |
| 1481 | + yH = (in - f->R2*f->s1 - f->g*f->s1 - f->s2) * f->h; | |
| 1482 | + | |
| 1483 | + // compute bandpass output by applying 1st integrator to highpass output: | |
| 1484 | + yB = (f->g*yH) + f->s1; | |
| 1485 | + f->s1 = f->g*yH + yB; // state update in 1st integrator | |
| 1486 | + | |
| 1487 | + // compute lowpass output by applying 2nd integrator to bandpass output: | |
| 1488 | + yL = (f->g*yB) + f->s2; | |
| 1489 | + f->s2 = f->g*yB + yL; // state update in 2nd integrator | |
| 1490 | + | |
| 1491 | + //according to the Vadim paper, we could add saturation to this model by adding a tanh in the integration stage. | |
| 1492 | + // | |
| 1493 | + //seems like that might look like this: | |
| 1494 | + // y = tanh(g*x) + s; // output computation | |
| 1495 | + // s = g*x + y; // state update | |
| 1496 | + | |
| 1497 | + //instead of this: | |
| 1498 | + // y = g*x + s; // output computation | |
| 1499 | + // s = g*x + y; // state update | |
| 1500 | + | |
| 1501 | + return f->cL*yL + f->cB*yB + f->cH*yH; | |
| 1502 | + | |
| 1503 | +} | |
| 1504 | + | |
| 1505 | + | |
| 1506 | +void tVZFilter_calcCoeffs (tVZFilter* const vf) | |
| 1507 | +{ | |
| 1508 | + | |
| 1509 | + _tVZFilter* f = *vf; | |
| 1510 | + f->g = tanf(PI * f->fc * f->inv_sr); // embedded integrator gain (Fig 3.11) | |
| 1511 | + | |
| 1512 | + switch( f->type ) | |
| 1513 | + { | |
| 1514 | + case Bypass: | |
| 1515 | + { | |
| 1516 | + f->R2 = f->invG; // can we use an arbitrary value here, for example R2 = 1? | |
| 1517 | + f->cL = 1.0f; | |
| 1518 | + f->cB = f->R2; | |
| 1519 | + f->cH = 1.0f; | |
| 1520 | + } | |
| 1521 | + break; | |
| 1522 | + case Lowpass: | |
| 1523 | + { | |
| 1524 | + f->R2 = f->invG; | |
| 1525 | + f->cL = 1.0f; f->cB = 0.0f; f->cH = 0.0f; | |
| 1526 | + } | |
| 1527 | + break; | |
| 1528 | + case Highpass: | |
| 1529 | + { | |
| 1530 | + f->R2 = f->invG; | |
| 1531 | + f->cL = 0.0f; f->cB = 0.0f; f->cH = 1.0f; | |
| 1532 | + } | |
| 1533 | + break; | |
| 1534 | + case BandpassSkirt: | |
| 1535 | + { | |
| 1536 | + f->R2 = f->invG; | |
| 1537 | + f->cL = 0.0f; f->cB = 1.0f; f->cH = 0.0f; | |
| 1538 | + } | |
| 1539 | + break; | |
| 1540 | + case BandpassPeak: | |
| 1541 | + { | |
| 1542 | + f->R2 = 2.0f*tVZFilter_BandwidthToR(vf, f->B); | |
| 1543 | + f->cL = 0.0f; f->cB = f->R2; f->cH = 0.0f; | |
| 1544 | + } | |
| 1545 | + break; | |
| 1546 | + case BandReject: | |
| 1547 | + { | |
| 1548 | + f->R2 = 2.0f*tVZFilter_BandwidthToR(vf, f->B); | |
| 1549 | + f->cL = 1.0f; f->cB = 0.0f; f->cH = 1.0f; | |
| 1550 | + } | |
| 1551 | + break; | |
| 1552 | + case Bell: | |
| 1553 | + { | |
| 1554 | + float fl = f->fc*powf(2.0f, (-f->B)*0.5f); // lower bandedge frequency (in Hz) | |
| 1555 | + float wl = tanf(PI*fl*f->inv_sr); // warped radian lower bandedge frequency /(2*fs) | |
| 1556 | + float r = f->g/wl; | |
| 1557 | + r *= r; // warped frequency ratio wu/wl == (wc/wl)^2 where wu is the | |
| 1558 | + // warped upper bandedge, wc the center | |
| 1559 | + f->R2 = 2.0f*sqrtf(((r*r+1.0f)/r-2.0f)/(4.0f*f->G)); | |
| 1560 | + f->cL = 1.0f; f->cB = f->R2*f->G; f->cH = 1.0f; | |
| 1561 | + } | |
| 1562 | + break; | |
| 1563 | + case Lowshelf: | |
| 1564 | + { | |
| 1565 | + float A = sqrtf(f->G); | |
| 1566 | + f->g /= sqrtf(A); // scale SVF-cutoff frequency for shelvers | |
| 1567 | + f->R2 = 2*sinhf(f->B*logf(2.0f)*0.5f); | |
| 1568 | + f->cL = f->G; f->cB = f->R2*A; f->cH = 1.0f; | |
| 1569 | + } | |
| 1570 | + break; | |
| 1571 | + case Highshelf: | |
| 1572 | + { | |
| 1573 | + float A = sqrtf(f->G); | |
| 1574 | + f->g *= sqrtf(A); // scale SVF-cutoff frequency for shelvers | |
| 1575 | + f->R2 = 2.0f*sinhf(f->B*logf(2.0f)*0.5f); | |
| 1576 | + f->cL = 1.0f; f->cB = f->R2*A; f->cH = f->G; | |
| 1577 | + } | |
| 1578 | + break; | |
| 1579 | + case Allpass: | |
| 1580 | + { | |
| 1581 | + f->R2 = 2.0f*tVZFilter_BandwidthToR(vf, f->B); | |
| 1582 | + f->cL = 1.0f; f->cB = -f->R2; f->cH = 1.0f; | |
| 1583 | + } | |
| 1584 | + break; | |
| 1585 | + | |
| 1586 | + // experimental - maybe we must find better curves for cL, cB, cH: | |
| 1587 | + case Morph: | |
| 1588 | + { | |
| 1589 | + f->R2 = f->invG; | |
| 1590 | + float x = 2.0f*f->m-1.0f; | |
| 1591 | + | |
| 1592 | + f->cL = maximum(-x, 0.0f); /*cL *= cL;*/ | |
| 1593 | + f->cH = minimum( x, 0.0f); /*cH *= cH;*/ | |
| 1594 | + f->cB = 1.0f-x*x; | |
| 1595 | + | |
| 1596 | + // bottom line: we need to test different versions for how they feel when tweaking the | |
| 1597 | + // morph parameter | |
| 1598 | + | |
| 1599 | + // this scaling ensures constant magnitude at the cutoff point (we divide the coefficients by | |
| 1600 | + // the magnitude response value at the cutoff frequency and scale back by the gain): | |
| 1601 | + float s = f->G * sqrtf((f->R2*f->R2) / (f->cL*f->cL + f->cB*f->cB + f->cH*f->cH - 2.0f*f->cL*f->cH)); | |
| 1602 | + f->cL *= s; f->cB *= s; f->cH *= s; | |
| 1603 | + } | |
| 1604 | + break; | |
| 1605 | + | |
| 1606 | + } | |
| 1607 | + | |
| 1608 | + f->h = 1.0f / (1.0f + f->R2*f->g + f->g*f->g); // factor for feedback precomputation | |
| 1609 | +} | |
| 1610 | + | |
| 1611 | + | |
| 1612 | +void tVZFilter_setBandwidth (tVZFilter* const vf, float B) | |
| 1613 | +{ | |
| 1614 | + _tVZFilter* f = *vf; | |
| 1615 | + f->B = LEAF_clip(0.0f, B, 100.0f); | |
| 1616 | + tVZFilter_calcCoeffs(vf); | |
| 1617 | +} | |
| 1618 | +void tVZFilter_setFreq (tVZFilter* const vf, float freq) | |
| 1619 | +{ | |
| 1620 | + _tVZFilter* f = *vf; | |
| 1621 | + f->fc = LEAF_clip(0.0f, freq, 0.5f*leaf.sampleRate); | |
| 1622 | + tVZFilter_calcCoeffs(vf); | |
| 1623 | +} | |
| 1624 | +void tVZFilter_setFreqAndBandwidth (tVZFilter* const vf, float freq, float bw) | |
| 1625 | +{ | |
| 1626 | + _tVZFilter* f = *vf; | |
| 1627 | + f->B = LEAF_clip(0.0f,bw, 100.0f); | |
| 1628 | + f->fc = LEAF_clip(0.0f, freq, 0.5f*leaf.sampleRate); | |
| 1629 | + tVZFilter_calcCoeffs(vf); | |
| 1630 | +} | |
| 1631 | + | |
| 1632 | +void tVZFilter_setGain (tVZFilter* const vf, float gain) | |
| 1633 | +{ | |
| 1634 | + _tVZFilter* f = *vf; | |
| 1635 | + f->G = LEAF_clip(0.000001f, gain, 100.0f); | |
| 1636 | + f->invG = 1.0f/f->G; | |
| 1637 | + tVZFilter_calcCoeffs(vf); | |
| 1638 | +} | |
| 1639 | + | |
| 1640 | +void tVZFilter_setMorph (tVZFilter* const vf, float morph) | |
| 1641 | +{ | |
| 1642 | + _tVZFilter* f = *vf; | |
| 1643 | + f->m = LEAF_clip(0.0f, morph, 1.0f); | |
| 1644 | + tVZFilter_calcCoeffs(vf); | |
| 1645 | +} | |
| 1646 | + | |
| 1647 | +void tVZFilter_setType (tVZFilter* const vf, VZFilterType type) | |
| 1648 | +{ | |
| 1649 | + _tVZFilter* f = *vf; | |
| 1650 | + f->type = type; | |
| 1651 | + tVZFilter_calcCoeffs(vf); | |
| 1652 | +} | |
| 1653 | + | |
| 1654 | +float tVZFilter_BandwidthToR(tVZFilter* const vf, float B) | |
| 1655 | +{ | |
| 1656 | + _tVZFilter* f = *vf; | |
| 1657 | + float fl = f->fc*powf(2.0f, -B*0.5f); // lower bandedge frequency (in Hz) | |
| 1658 | + float gl = tanf(PI*fl*f->inv_sr); // warped radian lower bandedge frequency /(2*fs) | |
| 1659 | + float r = gl/f->g; // ratio between warped lower bandedge- and center-frequencies | |
| 1660 | + // unwarped: r = pow(2, -B/2) -> approximation for low | |
| 1661 | + // center-frequencies | |
| 1662 | + return sqrtf((1.0f-r*r)*(1.0f-r*r)/(4.0f*r*r)); | |
| 1663 | +} | |
| 1664 | + | |
| 1665 | + | |
| 1666 | + | |
| 1667 | +void tDiodeFilter_init (tDiodeFilter* const vf, float cutoff, float resonance) | |
| 1668 | +{ | |
| 1669 | + tDiodeFilter_initToPool(vf, cutoff, resonance, &leaf.mempool); | |
| 1670 | +} | |
| 1671 | + | |
| 1672 | +void tDiodeFilter_free (tDiodeFilter* const vf) | |
| 1673 | +{ | |
| 1674 | + tDiodeFilter_freeFromPool(vf, &leaf.mempool); | |
| 1675 | +} | |
| 1676 | +void tDiodeFilter_initToPool (tDiodeFilter* const vf, float cutoff, float resonance, tMempool* const mp) | |
| 1677 | +{ | |
| 1678 | + | |
| 1679 | + _tMempool* m = *mp; | |
| 1680 | + _tDiodeFilter* f = *vf = (_tDiodeFilter*) mpool_alloc(sizeof(_tDiodeFilter), m); | |
| 1681 | + // initialization (the resonance factor is between 0 and 8 according to the article) | |
| 1682 | + f->f = tan(PI * cutoff/leaf.sampleRate); | |
| 1683 | + f->r = (7.f * resonance + 0.5f); | |
| 1684 | + f->Vt = 0.5f; | |
| 1685 | + f->n = 1.836f; | |
| 1686 | + f->zi = 0.0f; //previous input value | |
| 1687 | + f->gamma = f->Vt*f->n; | |
| 1688 | + f->s0 = 0.01f; | |
| 1689 | + f->s1 = 0.02f; | |
| 1690 | + f->s2 = 0.03f; | |
| 1691 | + f->s3 = 0.04f; | |
| 1692 | + f->g0inv = 1.f/(2.f*f->Vt); | |
| 1693 | + f->g1inv = 1.f/(2.f*f->gamma); | |
| 1694 | + f->g2inv = 1.f/(6.f*f->gamma); | |
| 1695 | + | |
| 1696 | + | |
| 1697 | +} | |
| 1698 | +void tDiodeFilter_freeFromPool (tDiodeFilter* const vf, tMempool* const mp) | |
| 1699 | +{ | |
| 1700 | + _tMempool* m = *mp; | |
| 1701 | + _tDiodeFilter* f = *vf = (_tDiodeFilter*) mpool_alloc(sizeof(_tDiodeFilter), m); | |
| 1702 | + mpool_free((char*)f, m); | |
| 1703 | +} | |
| 1704 | + | |
| 1705 | +float tanhXdX(float x) | |
| 1706 | +{ | |
| 1707 | + float a = x*x; | |
| 1708 | + // IIRC I got this as Pade-approx for tanh(sqrt(x))/sqrt(x) | |
| 1709 | + return ((a + 105.0f)*a + 945.0f) / ((15.0f*a + 420.0f)*a + 945.0f); | |
| 1710 | +} | |
| 1711 | + | |
| 1712 | +float tDiodeFilter_tick (tDiodeFilter* const vf, float in) | |
| 1713 | +{ | |
| 1714 | + _tDiodeFilter* f = *vf; | |
| 1715 | + | |
| 1716 | + // the input x[n+1] is given by 'in', and x[n] by zi | |
| 1717 | + // input with half delay | |
| 1718 | + float ih = 0.5f * (in + f->zi); | |
| 1719 | + | |
| 1720 | + // evaluate the non-linear factors | |
| 1721 | + float t0 = f->f*tanhXdX((ih - f->r * f->s3)*f->g0inv)*f->g0inv; | |
| 1722 | + float t1 = f->f*tanhXdX((f->s1-f->s0)*f->g1inv)*f->g1inv; | |
| 1723 | + float t2 = f->f*tanhXdX((f->s2-f->s1)*f->g1inv)*f->g1inv; | |
| 1724 | + float t3 = f->f*tanhXdX((f->s3-f->s2)*f->g1inv)*f->g1inv; | |
| 1725 | + float t4 = f->f*tanhXdX((f->s3)*f->g2inv)*f->g2inv; | |
| 1726 | + | |
| 1727 | + | |
| 1728 | + | |
| 1729 | + // This formula gives the result for y3 thanks to MATLAB | |
| 1730 | + float y3 = (f->s2 + f->s3 + t2*(f->s1 + f->s2 + f->s3 + t1*(f->s0 + f->s1 + f->s2 + f->s3 + t0*in)) + t1*(2.0f*f->s2 + 2.0f*f->s3))*t3 + f->s3 + 2.0f*f->s3*t1 + t2*(2.0f*f->s3 + 3.0f*f->s3*t1); | |
| 1731 | +// if (isnan(y3)) | |
| 1732 | +// { | |
| 1733 | +// __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400); | |
| 1734 | +// } | |
| 1735 | + float tempy3denom = (t4 + t1*(2.0f*t4 + 4.0f) + t2*(t4 + t1*(t4 + f->r*t0 + 4.0f) + 3.0f) + 2.0f)*t3 + t4 + t1*(2.0f*t4 + 2.0f) + t2*(2.0f*t4 + t1*(3.0f*t4 + 3.0f) + 2.0f) + 1.0f; | |
| 1736 | +// if (isnan(tempy3denom)) | |
| 1737 | +// { | |
| 1738 | +// __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400); | |
| 1739 | +// } | |
| 1740 | + if (tempy3denom == 0.0f) | |
| 1741 | + { | |
| 1742 | + tempy3denom = 0.000001f; | |
| 1743 | + } | |
| 1744 | + y3 = y3 / tempy3denom; | |
| 1745 | +// if (isnan(y3)) | |
| 1746 | +// { | |
| 1747 | +// __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400); | |
| 1748 | +// } | |
| 1749 | + if (t1 == 0.0f) | |
| 1750 | + { | |
| 1751 | + t1 = 0.000001f; | |
| 1752 | + } | |
| 1753 | + if (t2 == 0.0f) | |
| 1754 | + { | |
| 1755 | + t2 = 0.000001f; | |
| 1756 | + } | |
| 1757 | + if (t3 == 0.0f) | |
| 1758 | + { | |
| 1759 | + t3 = 0.000001f; | |
| 1760 | + } | |
| 1761 | + // Other outputs | |
| 1762 | + float y2 = (f->s3 - (1+t4+t3)*y3) / (-t3); | |
| 1763 | + float y1 = (f->s2 - (1+t3+t2)*y2 + t3*y3) / (-t2); | |
| 1764 | + float y0 = (f->s1 - (1+t2+t1)*y1 + t2*y2) / (-t1); | |
| 1765 | + float xx = (in - f->r*y3); | |
| 1766 | + | |
| 1767 | + // update state | |
| 1768 | + f->s0 += 2.0f * (t0*xx + t1*(y1-y0)); | |
| 1769 | +// if (isnan(f->s0)) | |
| 1770 | +// { | |
| 1771 | +// __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400); | |
| 1772 | +// } | |
| 1773 | + | |
| 1774 | +// if (isinf(f->s0)) | |
| 1775 | +// { | |
| 1776 | +// __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400); | |
| 1777 | +// } | |
| 1778 | + f->s1 += 2.0f * (t2*(y2-y1) - t1*(y1-y0)); | |
| 1779 | + f->s2 += 2.0f * (t3*(y3-y2) - t2*(y2-y1)); | |
| 1780 | + f->s3 += 2.0f * (-t4*(y3) - t3*(y3-y2)); | |
| 1781 | + | |
| 1782 | + f->zi = in; | |
| 1783 | + return y3*f->r; | |
| 1784 | + | |
| 1785 | +} | |
| 1786 | + | |
| 1787 | + | |
| 1788 | + | |
| 1789 | +void tDiodeFilter_setFreq (tDiodeFilter* const vf, float cutoff) | |
| 1790 | +{ | |
| 1791 | + _tDiodeFilter* f = *vf; | |
| 1792 | + f->f = tanf(PI * LEAF_clip(10.0f, cutoff, 20000.0f)*leaf.invSampleRate); | |
| 1793 | +} | |
| 1794 | + | |
| 1795 | + | |
| 1796 | +void tDiodeFilter_setQ (tDiodeFilter* const vf, float resonance) | |
| 1797 | +{ | |
| 1798 | + _tDiodeFilter* f = *vf; | |
| 1799 | + f->r = LEAF_clip(0.5, (7.f * resonance + 0.5f), 8.0f); | |
| 1800 | + | |
| 1801 | +} | |
| 1802 | + | |
| @@ -0,0 +1,706 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-instruments.c | |
| 4 | + Created: 30 Nov 2018 10:24:21am | |
| 5 | + Author: airship | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-instruments.h" | |
| 12 | + | |
| 13 | +#else | |
| 14 | + | |
| 15 | +#include "../Inc/leaf-instruments.h" | |
| 16 | + | |
| 17 | +#endif | |
| 18 | + | |
| 19 | +// ----------------- COWBELL ----------------------------// | |
| 20 | + | |
| 21 | +void t808Cowbell_init(t808Cowbell* const cowbellInst, int useStick) | |
| 22 | +{ | |
| 23 | + _t808Cowbell* cowbell = *cowbellInst = (_t808Cowbell*) leaf_alloc(sizeof(_t808Cowbell)); | |
| 24 | + | |
| 25 | + tSquare_init(&cowbell->p[0]); | |
| 26 | + tSquare_setFreq(&cowbell->p[0], 540.0f); | |
| 27 | + | |
| 28 | + tSquare_init(&cowbell->p[1]); | |
| 29 | + tSquare_setFreq(&cowbell->p[1], 1.48148f * 540.0f); | |
| 30 | + | |
| 31 | + cowbell->oscMix = 0.5f; | |
| 32 | + | |
| 33 | + tSVF_init(&cowbell->bandpassOsc, SVFTypeBandpass, 2500, 1.0f); | |
| 34 | + | |
| 35 | + tSVF_init(&cowbell->bandpassStick, SVFTypeBandpass, 1800, 1.0f); | |
| 36 | + | |
| 37 | + tEnvelope_init(&cowbell->envGain, 5.0f, 100.0f, OFALSE); | |
| 38 | + | |
| 39 | + tEnvelope_init(&cowbell->envFilter, 5.0, 100.0f, OFALSE); | |
| 40 | + | |
| 41 | + tHighpass_init(&cowbell->highpass, 1000.0f); | |
| 42 | + | |
| 43 | + tNoise_init(&cowbell->stick, WhiteNoise); | |
| 44 | + | |
| 45 | + tEnvelope_init(&cowbell->envStick, 5.0f, 5.0f, 0); | |
| 46 | + | |
| 47 | + cowbell->useStick = useStick; | |
| 48 | +} | |
| 49 | + | |
| 50 | +void t808Cowebell_free(t808Cowbell* const cowbellInst) | |
| 51 | +{ | |
| 52 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 53 | + | |
| 54 | + tSquare_free(&cowbell->p[0]); | |
| 55 | + tSquare_free(&cowbell->p[1]); | |
| 56 | + tSVF_free(&cowbell->bandpassOsc); | |
| 57 | + tSVF_free(&cowbell->bandpassStick); | |
| 58 | + tEnvelope_free(&cowbell->envGain); | |
| 59 | + tEnvelope_free(&cowbell->envFilter); | |
| 60 | + tHighpass_free(&cowbell->highpass); | |
| 61 | + tNoise_free(&cowbell->stick); | |
| 62 | + tEnvelope_free(&cowbell->envStick); | |
| 63 | + leaf_free((char*)cowbell); | |
| 64 | +} | |
| 65 | + | |
| 66 | +void t808Cowbell_initToPool (t808Cowbell* const cowbellInst, int useStick, tMempool* const mp) | |
| 67 | +{ | |
| 68 | + _tMempool* m = *mp; | |
| 69 | + _t808Cowbell* cowbell = *cowbellInst = (_t808Cowbell*) mpool_alloc(sizeof(_t808Cowbell), m); | |
| 70 | + | |
| 71 | + tSquare_initToPool(&cowbell->p[0], mp); | |
| 72 | + tSquare_setFreq(&cowbell->p[0], 540.0f); | |
| 73 | + | |
| 74 | + tSquare_initToPool(&cowbell->p[1], mp); | |
| 75 | + tSquare_setFreq(&cowbell->p[1], 1.48148f * 540.0f); | |
| 76 | + | |
| 77 | + cowbell->oscMix = 0.5f; | |
| 78 | + | |
| 79 | + tSVF_initToPool(&cowbell->bandpassOsc, SVFTypeBandpass, 2500, 1.0f, mp); | |
| 80 | + | |
| 81 | + tSVF_initToPool(&cowbell->bandpassStick, SVFTypeBandpass, 1800, 1.0f, mp); | |
| 82 | + | |
| 83 | + tEnvelope_initToPool(&cowbell->envGain, 5.0f, 100.0f, OFALSE, mp); | |
| 84 | + | |
| 85 | + tEnvelope_initToPool(&cowbell->envFilter, 5.0, 100.0f, OFALSE, mp); | |
| 86 | + | |
| 87 | + tHighpass_initToPool(&cowbell->highpass, 1000.0f, mp); | |
| 88 | + | |
| 89 | + tNoise_initToPool(&cowbell->stick, WhiteNoise, mp); | |
| 90 | + | |
| 91 | + tEnvelope_initToPool(&cowbell->envStick, 5.0f, 5.0f, 0, mp); | |
| 92 | + | |
| 93 | + cowbell->useStick = useStick; | |
| 94 | +} | |
| 95 | + | |
| 96 | +void t808Cowbell_freeFromPool (t808Cowbell* const cowbellInst, tMempool* const mp) | |
| 97 | +{ | |
| 98 | + _tMempool* m = *mp; | |
| 99 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 100 | + | |
| 101 | + tSquare_freeFromPool(&cowbell->p[0], mp); | |
| 102 | + tSquare_freeFromPool(&cowbell->p[1], mp); | |
| 103 | + tSVF_freeFromPool(&cowbell->bandpassOsc, mp); | |
| 104 | + tSVF_freeFromPool(&cowbell->bandpassStick, mp); | |
| 105 | + tEnvelope_freeFromPool(&cowbell->envGain, mp); | |
| 106 | + tEnvelope_freeFromPool(&cowbell->envFilter, mp); | |
| 107 | + tHighpass_freeFromPool(&cowbell->highpass, mp); | |
| 108 | + tNoise_freeFromPool(&cowbell->stick, mp); | |
| 109 | + tEnvelope_freeFromPool(&cowbell->envStick, mp); | |
| 110 | + mpool_free((char*)cowbell, m); | |
| 111 | +} | |
| 112 | + | |
| 113 | +void t808Cowbell_on(t808Cowbell* const cowbellInst, float vel) | |
| 114 | +{ | |
| 115 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 116 | + | |
| 117 | + tEnvelope_on(&cowbell->envGain, vel); | |
| 118 | + | |
| 119 | + if (cowbell->useStick) | |
| 120 | + tEnvelope_on(&cowbell->envStick,vel); | |
| 121 | +} | |
| 122 | + | |
| 123 | +float t808Cowbell_tick(t808Cowbell* const cowbellInst) | |
| 124 | +{ | |
| 125 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 126 | + | |
| 127 | + float sample = 0.0f; | |
| 128 | + | |
| 129 | + // Mix oscillators. | |
| 130 | + sample = (cowbell->oscMix * tSquare_tick(&cowbell->p[0])) + ((1.0f-cowbell->oscMix) * tSquare_tick(&cowbell->p[1])); | |
| 131 | + | |
| 132 | + // Filter dive and filter. | |
| 133 | + tSVF_setFreq(&cowbell->bandpassOsc, cowbell->filterCutoff + 1000.0f * tEnvelope_tick(&cowbell->envFilter)); | |
| 134 | + sample = tSVF_tick(&cowbell->bandpassOsc,sample); | |
| 135 | + | |
| 136 | + sample *= (0.9f * tEnvelope_tick(&cowbell->envGain)); | |
| 137 | + | |
| 138 | + if (cowbell->useStick) | |
| 139 | + sample += (0.1f * tEnvelope_tick(&cowbell->envStick) * tSVF_tick(&cowbell->bandpassStick, tNoise_tick(&cowbell->stick))); | |
| 140 | + | |
| 141 | + | |
| 142 | + sample = tHighpass_tick(&cowbell->highpass, sample); | |
| 143 | + | |
| 144 | + return sample; | |
| 145 | +} | |
| 146 | + | |
| 147 | +void t808Cowbell_setDecay(t808Cowbell* const cowbellInst, float decay) | |
| 148 | +{ | |
| 149 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 150 | + tEnvelope_setDecay(&cowbell->envGain,decay); | |
| 151 | +} | |
| 152 | + | |
| 153 | +void t808Cowbell_setHighpassFreq(t808Cowbell *cowbellInst, float freq) | |
| 154 | +{ | |
| 155 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 156 | + tHighpass_setFreq(&cowbell->highpass,freq); | |
| 157 | +} | |
| 158 | + | |
| 159 | +void t808Cowbell_setBandpassFreq(t808Cowbell* const cowbellInst, float freq) | |
| 160 | +{ | |
| 161 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 162 | + cowbell->filterCutoff = freq; | |
| 163 | +} | |
| 164 | + | |
| 165 | +void t808Cowbell_setFreq(t808Cowbell* const cowbellInst, float freq) | |
| 166 | +{ | |
| 167 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 168 | + tSquare_setFreq(&cowbell->p[0],freq); | |
| 169 | + tSquare_setFreq(&cowbell->p[1],1.48148f*freq); | |
| 170 | +} | |
| 171 | + | |
| 172 | +void t808Cowbell_setOscMix(t808Cowbell* const cowbellInst, float oscMix) | |
| 173 | +{ | |
| 174 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 175 | + cowbell->oscMix = oscMix; | |
| 176 | +} | |
| 177 | + | |
| 178 | +void t808Cowbell_setStick(t808Cowbell* const cowbellInst, int useStick) | |
| 179 | +{ | |
| 180 | + _t808Cowbell* cowbell = *cowbellInst; | |
| 181 | + cowbell->useStick = useStick; | |
| 182 | +} | |
| 183 | + | |
| 184 | +// ----------------- HIHAT ----------------------------// | |
| 185 | + | |
| 186 | +void t808Hihat_init(t808Hihat* const hihatInst) | |
| 187 | +{ | |
| 188 | + _t808Hihat* hihat = *hihatInst = (_t808Hihat*) leaf_alloc(sizeof(_t808Hihat)); | |
| 189 | + | |
| 190 | + for (int i = 0; i < 6; i++) | |
| 191 | + { | |
| 192 | + tSquare_init(&hihat->p[i]); | |
| 193 | + } | |
| 194 | + | |
| 195 | + tNoise_init(&hihat->stick, PinkNoise); | |
| 196 | + tNoise_init(&hihat->n, WhiteNoise); | |
| 197 | + | |
| 198 | + // need to fix SVF to be generic | |
| 199 | + tSVF_init(&hihat->bandpassStick, SVFTypeBandpass,2500.0f,1.2f); | |
| 200 | + tSVF_init(&hihat->bandpassOsc, SVFTypeBandpass,3500.0f,0.3f); | |
| 201 | + | |
| 202 | + tEnvelope_init(&hihat->envGain, 0.0f, 50.0f, OFALSE); | |
| 203 | + tEnvelope_init(&hihat->envStick, 0.0f, 7.0f, OFALSE); | |
| 204 | + | |
| 205 | + | |
| 206 | + tHighpass_init(&hihat->highpass, 7000.0f); | |
| 207 | + | |
| 208 | + hihat->freq = 40.0f; | |
| 209 | + hihat->stretch = 0.0f; | |
| 210 | + | |
| 211 | + tSquare_setFreq(&hihat->p[0], 2.0f * hihat->freq); | |
| 212 | + tSquare_setFreq(&hihat->p[1], 3.00f * hihat->freq); | |
| 213 | + tSquare_setFreq(&hihat->p[2], 4.16f * hihat->freq); | |
| 214 | + tSquare_setFreq(&hihat->p[3], 5.43f * hihat->freq); | |
| 215 | + tSquare_setFreq(&hihat->p[4], 6.79f * hihat->freq); | |
| 216 | + tSquare_setFreq(&hihat->p[5], 8.21f * hihat->freq); | |
| 217 | +} | |
| 218 | + | |
| 219 | +void t808Hihat_free(t808Hihat* const hihatInst) | |
| 220 | +{ | |
| 221 | + _t808Hihat* hihat = *hihatInst; | |
| 222 | + | |
| 223 | + for (int i = 0; i < 6; i++) | |
| 224 | + { | |
| 225 | + tSquare_free(&hihat->p[i]); | |
| 226 | + } | |
| 227 | + | |
| 228 | + tNoise_free(&hihat->stick); | |
| 229 | + tNoise_free(&hihat->n); | |
| 230 | + | |
| 231 | + // need to fix SVF to be generic | |
| 232 | + tSVF_free(&hihat->bandpassStick); | |
| 233 | + tSVF_free(&hihat->bandpassOsc); | |
| 234 | + tEnvelope_free(&hihat->envGain); | |
| 235 | + tEnvelope_free(&hihat->envStick); | |
| 236 | + | |
| 237 | + tHighpass_free(&hihat->highpass); | |
| 238 | + | |
| 239 | + leaf_free((char*)hihat); | |
| 240 | +} | |
| 241 | + | |
| 242 | +void t808Hihat_initToPool (t808Hihat* const hihatInst, tMempool* const mp) | |
| 243 | +{ | |
| 244 | + _tMempool* m = *mp; | |
| 245 | + _t808Hihat* hihat = *hihatInst = (_t808Hihat*) mpool_alloc(sizeof(_t808Hihat), m); | |
| 246 | + | |
| 247 | + for (int i = 0; i < 6; i++) | |
| 248 | + { | |
| 249 | + tSquare_initToPool(&hihat->p[i], mp); | |
| 250 | + } | |
| 251 | + | |
| 252 | + tNoise_initToPool(&hihat->stick, PinkNoise, mp); | |
| 253 | + tNoise_initToPool(&hihat->n, WhiteNoise, mp); | |
| 254 | + | |
| 255 | + // need to fix SVF to be generic | |
| 256 | + tSVF_initToPool(&hihat->bandpassStick, SVFTypeBandpass,2500.0f,1.2f, mp); | |
| 257 | + tSVF_initToPool(&hihat->bandpassOsc, SVFTypeBandpass,3500.0f,0.3f, mp); | |
| 258 | + | |
| 259 | + tEnvelope_initToPool(&hihat->envGain, 0.0f, 50.0f, OFALSE, mp); | |
| 260 | + tEnvelope_initToPool(&hihat->envStick, 0.0f, 7.0f, OFALSE, mp); | |
| 261 | + | |
| 262 | + | |
| 263 | + tHighpass_initToPool(&hihat->highpass, 7000.0f, mp); | |
| 264 | + | |
| 265 | + hihat->freq = 40.0f; | |
| 266 | + hihat->stretch = 0.0f; | |
| 267 | + | |
| 268 | + tSquare_setFreq(&hihat->p[0], 2.0f * hihat->freq); | |
| 269 | + tSquare_setFreq(&hihat->p[1], 3.00f * hihat->freq); | |
| 270 | + tSquare_setFreq(&hihat->p[2], 4.16f * hihat->freq); | |
| 271 | + tSquare_setFreq(&hihat->p[3], 5.43f * hihat->freq); | |
| 272 | + tSquare_setFreq(&hihat->p[4], 6.79f * hihat->freq); | |
| 273 | + tSquare_setFreq(&hihat->p[5], 8.21f * hihat->freq); | |
| 274 | +} | |
| 275 | + | |
| 276 | +void t808Hihat_freeFromPool (t808Hihat* const hihatInst, tMempool* const mp) | |
| 277 | +{ | |
| 278 | + _tMempool* m = *mp; | |
| 279 | + _t808Hihat* hihat = *hihatInst; | |
| 280 | + | |
| 281 | + for (int i = 0; i < 6; i++) | |
| 282 | + { | |
| 283 | + tSquare_freeFromPool(&hihat->p[i], mp); | |
| 284 | + } | |
| 285 | + | |
| 286 | + tNoise_freeFromPool(&hihat->stick, mp); | |
| 287 | + tNoise_freeFromPool(&hihat->n, mp); | |
| 288 | + | |
| 289 | + // need to fix SVF to be generic | |
| 290 | + tSVF_freeFromPool(&hihat->bandpassStick, mp); | |
| 291 | + tSVF_freeFromPool(&hihat->bandpassOsc, mp); | |
| 292 | + tEnvelope_freeFromPool(&hihat->envGain, mp); | |
| 293 | + tEnvelope_freeFromPool(&hihat->envStick, mp); | |
| 294 | + | |
| 295 | + tHighpass_freeFromPool(&hihat->highpass, mp); | |
| 296 | + | |
| 297 | + mpool_free((char*)hihat, m); | |
| 298 | +} | |
| 299 | + | |
| 300 | +void t808Hihat_on(t808Hihat* const hihatInst, float vel) | |
| 301 | +{ | |
| 302 | + _t808Hihat* hihat = *hihatInst; | |
| 303 | + tEnvelope_on(&hihat->envGain, vel); | |
| 304 | + tEnvelope_on(&hihat->envStick, vel); | |
| 305 | +} | |
| 306 | + | |
| 307 | +void t808Hihat_setOscNoiseMix(t808Hihat* const hihatInst, float oscNoiseMix) | |
| 308 | +{ | |
| 309 | + _t808Hihat* hihat = *hihatInst; | |
| 310 | + hihat->oscNoiseMix = oscNoiseMix; | |
| 311 | +} | |
| 312 | + | |
| 313 | +float t808Hihat_tick(t808Hihat* const hihatInst) | |
| 314 | +{ | |
| 315 | + _t808Hihat* hihat = *hihatInst; | |
| 316 | + | |
| 317 | + float sample = 0.0f; | |
| 318 | + float gainScale = 0.1666f; | |
| 319 | + | |
| 320 | + float myNoise = tNoise_tick(&hihat->n); | |
| 321 | + | |
| 322 | + tSquare_setFreq(&hihat->p[0], ((2.0f + hihat->stretch) * hihat->freq)); | |
| 323 | + tSquare_setFreq(&hihat->p[1], ((3.00f + hihat->stretch) * hihat->freq)); | |
| 324 | + tSquare_setFreq(&hihat->p[2], ((4.16f + hihat->stretch) * hihat->freq)); | |
| 325 | + tSquare_setFreq(&hihat->p[3], ((5.43f + hihat->stretch) * hihat->freq)); | |
| 326 | + tSquare_setFreq(&hihat->p[4], ((6.79f + hihat->stretch) * hihat->freq)); | |
| 327 | + tSquare_setFreq(&hihat->p[5], ((8.21f + hihat->stretch) * hihat->freq)); | |
| 328 | + | |
| 329 | + for (int i = 0; i < 6; i++) | |
| 330 | + { | |
| 331 | + sample += tSquare_tick(&hihat->p[i]); | |
| 332 | + } | |
| 333 | + | |
| 334 | + sample *= gainScale; | |
| 335 | + | |
| 336 | + sample = (hihat->oscNoiseMix * sample) + ((1.0f-hihat->oscNoiseMix) * myNoise); | |
| 337 | + | |
| 338 | + sample = tSVF_tick(&hihat->bandpassOsc, sample); | |
| 339 | + | |
| 340 | + float myGain = tEnvelope_tick(&hihat->envGain); | |
| 341 | + sample *= (myGain*myGain);//square the output gain envelope | |
| 342 | + sample = tHighpass_tick(&hihat->highpass, sample); | |
| 343 | + sample += ((0.5f * tEnvelope_tick(&hihat->envStick)) * tSVF_tick(&hihat->bandpassStick, tNoise_tick(&hihat->stick))); | |
| 344 | + sample = tanhf(sample * 2.0f); | |
| 345 | + | |
| 346 | + return sample; | |
| 347 | +} | |
| 348 | + | |
| 349 | +void t808Hihat_setDecay(t808Hihat* const hihatInst, float decay) | |
| 350 | +{ | |
| 351 | + _t808Hihat* hihat = *hihatInst; | |
| 352 | + tEnvelope_setDecay(&hihat->envGain,decay); | |
| 353 | +} | |
| 354 | + | |
| 355 | +void t808Hihat_setHighpassFreq(t808Hihat* const hihatInst, float freq) | |
| 356 | +{ | |
| 357 | + _t808Hihat* hihat = *hihatInst; | |
| 358 | + tHighpass_setFreq(&hihat->highpass,freq); | |
| 359 | +} | |
| 360 | + | |
| 361 | +void t808Hihat_setStretch(t808Hihat* const hihatInst, float stretch) | |
| 362 | +{ | |
| 363 | + _t808Hihat* hihat = *hihatInst; | |
| 364 | + hihat->stretch = stretch; | |
| 365 | +} | |
| 366 | + | |
| 367 | +void t808Hihat_setFM(t808Hihat* const hihatInst, float FM_amount) | |
| 368 | +{ | |
| 369 | + _t808Hihat* hihat = *hihatInst; | |
| 370 | + hihat->FM_amount = FM_amount; | |
| 371 | +} | |
| 372 | + | |
| 373 | +void t808Hihat_setOscBandpassFreq(t808Hihat* const hihatInst, float freq) | |
| 374 | +{ | |
| 375 | + _t808Hihat* hihat = *hihatInst; | |
| 376 | + tSVF_setFreq(&hihat->bandpassOsc,freq); | |
| 377 | +} | |
| 378 | + | |
| 379 | +void t808Hihat_setOscBandpassQ(t808Hihat* const hihatInst, float Q) | |
| 380 | +{ | |
| 381 | + _t808Hihat* hihat = *hihatInst; | |
| 382 | + tSVF_setQ(&hihat->bandpassOsc,Q); | |
| 383 | +} | |
| 384 | + | |
| 385 | +void t808Hihat_setStickBandPassFreq(t808Hihat* const hihatInst, float freq) | |
| 386 | +{ | |
| 387 | + _t808Hihat* hihat = *hihatInst; | |
| 388 | + tSVF_setFreq(&hihat->bandpassStick,freq); | |
| 389 | +} | |
| 390 | + | |
| 391 | +void t808Hihat_setStickBandPassQ(t808Hihat* const hihatInst, float Q) | |
| 392 | +{ | |
| 393 | + _t808Hihat* hihat = *hihatInst; | |
| 394 | + tSVF_setQ(&hihat->bandpassStick,Q); | |
| 395 | +} | |
| 396 | + | |
| 397 | +void t808Hihat_setOscFreq(t808Hihat* const hihatInst, float freq) | |
| 398 | +{ | |
| 399 | + _t808Hihat* hihat = *hihatInst; | |
| 400 | + hihat->freq = freq; | |
| 401 | +} | |
| 402 | + | |
| 403 | +// ----------------- SNARE ----------------------------// | |
| 404 | + | |
| 405 | +void t808Snare_init(t808Snare* const snareInst) | |
| 406 | +{ | |
| 407 | + _t808Snare* snare = *snareInst = (_t808Snare*) leaf_alloc(sizeof(_t808Snare)); | |
| 408 | + | |
| 409 | + float ratio[2] = {1.0, 1.5}; | |
| 410 | + for (int i = 0; i < 2; i++) | |
| 411 | + { | |
| 412 | + tTriangle_init(&snare->tone[i]); | |
| 413 | + | |
| 414 | + tTriangle_setFreq(&snare->tone[i], ratio[i] * 400.0f); | |
| 415 | + tSVF_init(&snare->toneLowpass[i], SVFTypeLowpass, 4000, 1.0f); | |
| 416 | + tEnvelope_init(&snare->toneEnvOsc[i], 0.0f, 50.0f, OFALSE); | |
| 417 | + tEnvelope_init(&snare->toneEnvGain[i], 1.0f, 150.0f, OFALSE); | |
| 418 | + tEnvelope_init(&snare->toneEnvFilter[i], 1.0f, 2000.0f, OFALSE); | |
| 419 | + | |
| 420 | + snare->toneGain[i] = 0.5f; | |
| 421 | + } | |
| 422 | + | |
| 423 | + snare->tone1Freq = ratio[0] * 100.0f; | |
| 424 | + snare->tone2Freq = ratio[1] * 100.0f; | |
| 425 | + snare->noiseFilterFreq = 3000.0f; | |
| 426 | + tNoise_init(&snare->noiseOsc, WhiteNoise); | |
| 427 | + tSVF_init(&snare->noiseLowpass, SVFTypeLowpass, 12000.0f, 0.8f); | |
| 428 | + tEnvelope_init(&snare->noiseEnvGain, 0.0f, 100.0f, OFALSE); | |
| 429 | + tEnvelope_init(&snare->noiseEnvFilter, 0.0f, 1000.0f, OFALSE); | |
| 430 | + snare->noiseGain = 1.0f; | |
| 431 | +} | |
| 432 | + | |
| 433 | +void t808Snare_free (t808Snare* const snareInst) | |
| 434 | +{ | |
| 435 | + _t808Snare* snare = *snareInst; | |
| 436 | + | |
| 437 | + for (int i = 0; i < 2; i++) | |
| 438 | + { | |
| 439 | + tTriangle_free(&snare->tone[i]); | |
| 440 | + tSVF_free(&snare->toneLowpass[i]); | |
| 441 | + tEnvelope_free(&snare->toneEnvOsc[i]); | |
| 442 | + tEnvelope_free(&snare->toneEnvGain[i]); | |
| 443 | + tEnvelope_free(&snare->toneEnvFilter[i]); | |
| 444 | + } | |
| 445 | + | |
| 446 | + tNoise_free(&snare->noiseOsc); | |
| 447 | + tSVF_free(&snare->noiseLowpass); | |
| 448 | + tEnvelope_free(&snare->noiseEnvGain); | |
| 449 | + tEnvelope_free(&snare->noiseEnvFilter); | |
| 450 | + | |
| 451 | + leaf_free((char*)snare); | |
| 452 | +} | |
| 453 | + | |
| 454 | +void t808Snare_initToPool (t808Snare* const snareInst, tMempool* const mp) | |
| 455 | +{ | |
| 456 | + _tMempool* m = *mp; | |
| 457 | + _t808Snare* snare = *snareInst = (_t808Snare*) mpool_alloc(sizeof(_t808Snare), m); | |
| 458 | + | |
| 459 | + float ratio[2] = {1.0, 1.5}; | |
| 460 | + for (int i = 0; i < 2; i++) | |
| 461 | + { | |
| 462 | + tTriangle_initToPool(&snare->tone[i], mp); | |
| 463 | + | |
| 464 | + tTriangle_setFreq(&snare->tone[i], ratio[i] * 400.0f); | |
| 465 | + tSVF_initToPool(&snare->toneLowpass[i], SVFTypeLowpass, 4000, 1.0f, mp); | |
| 466 | + tEnvelope_initToPool(&snare->toneEnvOsc[i], 0.0f, 50.0f, OFALSE, mp); | |
| 467 | + tEnvelope_initToPool(&snare->toneEnvGain[i], 1.0f, 150.0f, OFALSE, mp); | |
| 468 | + tEnvelope_initToPool(&snare->toneEnvFilter[i], 1.0f, 2000.0f, OFALSE, mp); | |
| 469 | + | |
| 470 | + snare->toneGain[i] = 0.5f; | |
| 471 | + } | |
| 472 | + | |
| 473 | + snare->tone1Freq = ratio[0] * 100.0f; | |
| 474 | + snare->tone2Freq = ratio[1] * 100.0f; | |
| 475 | + snare->noiseFilterFreq = 3000.0f; | |
| 476 | + tNoise_initToPool(&snare->noiseOsc, WhiteNoise, mp); | |
| 477 | + tSVF_initToPool(&snare->noiseLowpass, SVFTypeLowpass, 12000.0f, 0.8f, mp); | |
| 478 | + tEnvelope_initToPool(&snare->noiseEnvGain, 0.0f, 100.0f, OFALSE, mp); | |
| 479 | + tEnvelope_initToPool(&snare->noiseEnvFilter, 0.0f, 1000.0f, OFALSE, mp); | |
| 480 | + snare->noiseGain = 1.0f; | |
| 481 | +} | |
| 482 | + | |
| 483 | +void t808Snare_freeFromPool (t808Snare* const snareInst, tMempool* const mp) | |
| 484 | +{ | |
| 485 | + _tMempool* m = *mp; | |
| 486 | + _t808Snare* snare = *snareInst; | |
| 487 | + | |
| 488 | + for (int i = 0; i < 2; i++) | |
| 489 | + { | |
| 490 | + tTriangle_freeFromPool(&snare->tone[i], mp); | |
| 491 | + tSVF_freeFromPool(&snare->toneLowpass[i], mp); | |
| 492 | + tEnvelope_freeFromPool(&snare->toneEnvOsc[i], mp); | |
| 493 | + tEnvelope_freeFromPool(&snare->toneEnvGain[i], mp); | |
| 494 | + tEnvelope_freeFromPool(&snare->toneEnvFilter[i], mp); | |
| 495 | + } | |
| 496 | + | |
| 497 | + tNoise_freeFromPool(&snare->noiseOsc, mp); | |
| 498 | + tSVF_freeFromPool(&snare->noiseLowpass, mp); | |
| 499 | + tEnvelope_freeFromPool(&snare->noiseEnvGain, mp); | |
| 500 | + tEnvelope_freeFromPool(&snare->noiseEnvFilter, mp); | |
| 501 | + | |
| 502 | + mpool_free((char*)snare, m); | |
| 503 | +} | |
| 504 | + | |
| 505 | +void t808Snare_on(t808Snare* const snareInst, float vel) | |
| 506 | +{ | |
| 507 | + _t808Snare* snare = *snareInst; | |
| 508 | + | |
| 509 | + for (int i = 0; i < 2; i++) | |
| 510 | + { | |
| 511 | + tEnvelope_on(&snare->toneEnvOsc[i], vel); | |
| 512 | + tEnvelope_on(&snare->toneEnvGain[i], vel); | |
| 513 | + tEnvelope_on(&snare->toneEnvFilter[i], vel); | |
| 514 | + } | |
| 515 | + | |
| 516 | + tEnvelope_on(&snare->noiseEnvGain, vel); | |
| 517 | + tEnvelope_on(&snare->noiseEnvFilter, vel); | |
| 518 | +} | |
| 519 | + | |
| 520 | +void t808Snare_setTone1Freq(t808Snare* const snareInst, float freq) | |
| 521 | +{ | |
| 522 | + _t808Snare* snare = *snareInst; | |
| 523 | + snare->tone1Freq = freq; | |
| 524 | + tTriangle_setFreq(&snare->tone[0], freq); | |
| 525 | +} | |
| 526 | + | |
| 527 | +void t808Snare_setTone2Freq(t808Snare* const snareInst, float freq) | |
| 528 | +{ | |
| 529 | + _t808Snare* snare = *snareInst; | |
| 530 | + snare->tone2Freq = freq; | |
| 531 | + tTriangle_setFreq(&snare->tone[1],freq); | |
| 532 | +} | |
| 533 | + | |
| 534 | +void t808Snare_setTone1Decay(t808Snare* const snareInst, float decay) | |
| 535 | +{ | |
| 536 | + _t808Snare* snare = *snareInst; | |
| 537 | + tEnvelope_setDecay(&snare->toneEnvGain[0],decay); | |
| 538 | +} | |
| 539 | + | |
| 540 | +void t808Snare_setTone2Decay(t808Snare* const snareInst, float decay) | |
| 541 | +{ | |
| 542 | + _t808Snare* snare = *snareInst; | |
| 543 | + tEnvelope_setDecay(&snare->toneEnvGain[1],decay); | |
| 544 | +} | |
| 545 | + | |
| 546 | +void t808Snare_setNoiseDecay(t808Snare* const snareInst, float decay) | |
| 547 | +{ | |
| 548 | + _t808Snare* snare = *snareInst; | |
| 549 | + tEnvelope_setDecay(&snare->noiseEnvGain,decay); | |
| 550 | +} | |
| 551 | + | |
| 552 | +void t808Snare_setToneNoiseMix(t808Snare* const snareInst, float toneNoiseMix) | |
| 553 | +{ | |
| 554 | + _t808Snare* snare = *snareInst; | |
| 555 | + snare->toneNoiseMix = toneNoiseMix; | |
| 556 | +} | |
| 557 | + | |
| 558 | +void t808Snare_setNoiseFilterFreq(t808Snare* const snareInst, float noiseFilterFreq) | |
| 559 | +{ | |
| 560 | + _t808Snare* snare = *snareInst; | |
| 561 | + snare->noiseFilterFreq = noiseFilterFreq; | |
| 562 | +} | |
| 563 | + | |
| 564 | +void t808Snare_setNoiseFilterQ(t808Snare* const snareInst, float noiseFilterQ) | |
| 565 | +{ | |
| 566 | + _t808Snare* snare = *snareInst; | |
| 567 | + tSVF_setQ(&snare->noiseLowpass, noiseFilterQ); | |
| 568 | +} | |
| 569 | + | |
| 570 | +static float tone[2]; | |
| 571 | + | |
| 572 | +float t808Snare_tick(t808Snare* const snareInst) | |
| 573 | +{ | |
| 574 | + _t808Snare* snare = *snareInst; | |
| 575 | + | |
| 576 | + for (int i = 0; i < 2; i++) | |
| 577 | + { | |
| 578 | + tTriangle_setFreq(&snare->tone[i], snare->tone1Freq + (20.0f * tEnvelope_tick(&snare->toneEnvOsc[i]))); | |
| 579 | + tone[i] = tTriangle_tick(&snare->tone[i]); | |
| 580 | + | |
| 581 | + tSVF_setFreq(&snare->toneLowpass[i], 2000.0f + (500.0f * tEnvelope_tick(&snare->toneEnvFilter[i]))); | |
| 582 | + tone[i] = tSVF_tick(&snare->toneLowpass[i], tone[i]) * tEnvelope_tick(&snare->toneEnvGain[i]); | |
| 583 | + } | |
| 584 | + | |
| 585 | + float noise = tNoise_tick(&snare->noiseOsc); | |
| 586 | + tSVF_setFreq(&snare->noiseLowpass, snare->noiseFilterFreq + (1000.0f * tEnvelope_tick(&snare->noiseEnvFilter))); | |
| 587 | + noise = tSVF_tick(&snare->noiseLowpass, noise) * tEnvelope_tick(&snare->noiseEnvGain); | |
| 588 | + | |
| 589 | + float sample = (snare->toneNoiseMix)*(tone[0] * snare->toneGain[0] + tone[1] * snare->toneGain[1]) + (1.0f-snare->toneNoiseMix) * (noise * snare->noiseGain); | |
| 590 | + sample = tanhf(sample * 2.0f); | |
| 591 | + return sample; | |
| 592 | +} | |
| 593 | + | |
| 594 | +// ----------------- KICK ----------------------------// | |
| 595 | + | |
| 596 | +void t808Kick_init (t808Kick* const kickInst) | |
| 597 | +{ | |
| 598 | + _t808Kick* kick = *kickInst = (_t808Kick*) leaf_alloc(sizeof(_t808Kick)); | |
| 599 | + | |
| 600 | + tCycle_init(&kick->tone); | |
| 601 | + kick->toneInitialFreq = 40.0f; | |
| 602 | + kick->sighAmountInHz = 7.0f; | |
| 603 | + kick->chirpRatioMinusOne = 3.3f; | |
| 604 | + tCycle_setFreq(&kick->tone, 50.0f); | |
| 605 | + tSVF_init(&kick->toneLowpass, SVFTypeLowpass, 2000.0f, 0.5f); | |
| 606 | + tEnvelope_init(&kick->toneEnvOscChirp, 0.0f, 20.0f, OFALSE); | |
| 607 | + tEnvelope_init(&kick->toneEnvOscSigh, 0.0f, 2500.0f, OFALSE); | |
| 608 | + tEnvelope_init(&kick->toneEnvGain, 0.0f, 800.0f, OFALSE); | |
| 609 | + tNoise_init(&kick->noiseOsc, PinkNoise); | |
| 610 | + tEnvelope_init(&kick->noiseEnvGain, 0.0f, 1.0f, OFALSE); | |
| 611 | + kick->noiseGain = 0.3f; | |
| 612 | +} | |
| 613 | + | |
| 614 | +void t808Kick_free (t808Kick* const kickInst) | |
| 615 | +{ | |
| 616 | + _t808Kick* kick = *kickInst; | |
| 617 | + | |
| 618 | + tCycle_free(&kick->tone); | |
| 619 | + tSVF_free(&kick->toneLowpass); | |
| 620 | + tEnvelope_free(&kick->toneEnvOscChirp); | |
| 621 | + tEnvelope_free(&kick->toneEnvOscSigh); | |
| 622 | + tEnvelope_free(&kick->toneEnvGain); | |
| 623 | + tNoise_free(&kick->noiseOsc); | |
| 624 | + tEnvelope_free(&kick->noiseEnvGain); | |
| 625 | + | |
| 626 | + leaf_free((char*)kick); | |
| 627 | +} | |
| 628 | + | |
| 629 | +void t808Kick_initToPool (t808Kick* const kickInst, tMempool* const mp) | |
| 630 | +{ | |
| 631 | + _tMempool* m = *mp; | |
| 632 | + _t808Kick* kick = *kickInst = (_t808Kick*) mpool_alloc(sizeof(_t808Kick), m); | |
| 633 | + | |
| 634 | + tCycle_initToPool(&kick->tone, mp); | |
| 635 | + kick->toneInitialFreq = 40.0f; | |
| 636 | + kick->sighAmountInHz = 7.0f; | |
| 637 | + kick->chirpRatioMinusOne = 3.3f; | |
| 638 | + tCycle_setFreq(&kick->tone, 50.0f); | |
| 639 | + tSVF_initToPool(&kick->toneLowpass, SVFTypeLowpass, 2000.0f, 0.5f, mp); | |
| 640 | + tEnvelope_initToPool(&kick->toneEnvOscChirp, 0.0f, 20.0f, OFALSE, mp); | |
| 641 | + tEnvelope_initToPool(&kick->toneEnvOscSigh, 0.0f, 2500.0f, OFALSE, mp); | |
| 642 | + tEnvelope_initToPool(&kick->toneEnvGain, 0.0f, 800.0f, OFALSE, mp); | |
| 643 | + tNoise_initToPool(&kick->noiseOsc, PinkNoise, mp); | |
| 644 | + tEnvelope_initToPool(&kick->noiseEnvGain, 0.0f, 1.0f, OFALSE, mp); | |
| 645 | + kick->noiseGain = 0.3f; | |
| 646 | +} | |
| 647 | + | |
| 648 | +void t808Kick_freeFromPool (t808Kick* const kickInst, tMempool* const mp) | |
| 649 | +{ | |
| 650 | + _tMempool* m = *mp; | |
| 651 | + _t808Kick* kick = *kickInst; | |
| 652 | + | |
| 653 | + tCycle_freeFromPool(&kick->tone, mp); | |
| 654 | + tSVF_freeFromPool(&kick->toneLowpass, mp); | |
| 655 | + tEnvelope_freeFromPool(&kick->toneEnvOscChirp, mp); | |
| 656 | + tEnvelope_freeFromPool(&kick->toneEnvOscSigh, mp); | |
| 657 | + tEnvelope_freeFromPool(&kick->toneEnvGain, mp); | |
| 658 | + tNoise_freeFromPool(&kick->noiseOsc, mp); | |
| 659 | + tEnvelope_freeFromPool(&kick->noiseEnvGain, mp); | |
| 660 | + | |
| 661 | + mpool_free((char*)kick, m); | |
| 662 | +} | |
| 663 | + | |
| 664 | +float t808Kick_tick (t808Kick* const kickInst) | |
| 665 | +{ | |
| 666 | + _t808Kick* kick = *kickInst; | |
| 667 | + | |
| 668 | + tCycle_setFreq(&kick->tone, (kick->toneInitialFreq * (1.0f + (kick->chirpRatioMinusOne * tEnvelope_tick(&kick->toneEnvOscChirp)))) + (kick->sighAmountInHz * tEnvelope_tick(&kick->toneEnvOscSigh))); | |
| 669 | + float sample = tCycle_tick(&kick->tone) * tEnvelope_tick(&kick->toneEnvGain); | |
| 670 | + sample+= tNoise_tick(&kick->noiseOsc) * tEnvelope_tick(&kick->noiseEnvGain); | |
| 671 | + //add distortion here | |
| 672 | + sample = tSVF_tick(&kick->toneLowpass, sample); | |
| 673 | + return sample; | |
| 674 | +} | |
| 675 | + | |
| 676 | +void t808Kick_on (t808Kick* const kickInst, float vel) | |
| 677 | +{ | |
| 678 | + _t808Kick* kick = *kickInst; | |
| 679 | + tEnvelope_on(&kick->toneEnvOscChirp, vel); | |
| 680 | + tEnvelope_on(&kick->toneEnvOscSigh, vel); | |
| 681 | + tEnvelope_on(&kick->toneEnvGain, vel); | |
| 682 | + tEnvelope_on(&kick->noiseEnvGain, vel); | |
| 683 | + | |
| 684 | +} | |
| 685 | +void t808Kick_setToneFreq (t808Kick* const kickInst, float freq) | |
| 686 | +{ | |
| 687 | + _t808Kick* kick = *kickInst; | |
| 688 | + kick->toneInitialFreq = freq; | |
| 689 | + | |
| 690 | +} | |
| 691 | + | |
| 692 | +void t808Kick_setToneDecay (t808Kick* const kickInst, float decay) | |
| 693 | +{ | |
| 694 | + _t808Kick* kick = *kickInst; | |
| 695 | + tEnvelope_setDecay(&kick->toneEnvGain,decay); | |
| 696 | + tEnvelope_setDecay(&kick->toneEnvGain,decay * 3.0f); | |
| 697 | +} | |
| 698 | + | |
| 699 | +void t808Kick_setNoiseDecay (t808Kick* const kickInst, float decay); | |
| 700 | +void t808Kick_setSighAmount (t808Kick* const kickInst, float sigh); | |
| 701 | +void t808Kick_setChirpAmount (t808Kick* const kickInst, float chirp); | |
| 702 | +void t808Kick_setToneNoiseMix (t808Kick* const kickInst, float toneNoiseMix); | |
| 703 | +void t808Kick_setNoiseFilterFreq (t808Kick* const kickInst, float noiseFilterFreq); | |
| 704 | +void t808Kick_setNoiseFilterQ (t808Kick* const kickInst, float noiseFilterQ); | |
| 705 | + | |
| 706 | + | |
| @@ -0,0 +1,655 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-math.c | |
| 4 | + Created: 22 Jan 2017 7:02:56pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | + ==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-math.h" | |
| 12 | +#include "..\Inc\leaf-tables.h" | |
| 13 | + | |
| 14 | +#else | |
| 15 | + | |
| 16 | +#include "../Inc/leaf-math.h" | |
| 17 | +#include "../Inc/leaf-tables.h" | |
| 18 | + | |
| 19 | +#endif | |
| 20 | + | |
| 21 | + | |
| 22 | +#define EXPONENTIAL_TABLE_SIZE 65536 | |
| 23 | + | |
| 24 | +#define log10f_fast(x) (log2f_approx(x)*0.3010299956639812f) | |
| 25 | + | |
| 26 | +// This is a fast approximation to log2() found on http://openaudio.blogspot.com/2017/02/faster-log10-and-pow.html credited to this post https://community.arm.com/developer/tools-software/tools/f/armds-forum/4292/cmsis-dsp-new-functionality-proposal/22621#22621 | |
| 27 | +// Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E; | |
| 28 | +float log2f_approx(float X) { | |
| 29 | + float Y, F; | |
| 30 | + int E; | |
| 31 | + F = frexpf(fabsf(X), &E); | |
| 32 | + Y = 1.23149591368684f; | |
| 33 | + Y *= F; | |
| 34 | + Y += -4.11852516267426f; | |
| 35 | + Y *= F; | |
| 36 | + Y += 6.02197014179219f; | |
| 37 | + Y *= F; | |
| 38 | + Y += -3.13396450166353f; | |
| 39 | + Y += E; | |
| 40 | + return(Y); | |
| 41 | +} | |
| 42 | + | |
| 43 | +float interpolate3max(float *buf, const int peakindex) | |
| 44 | +{ | |
| 45 | + float a = buf[peakindex-1]; | |
| 46 | + float b = buf[peakindex]; | |
| 47 | + float c = buf[peakindex+1]; | |
| 48 | + float realpeak; | |
| 49 | + | |
| 50 | + realpeak = b + (float)0.125 * (c - a) * (c - a) / ((float)2. * b - a - c); | |
| 51 | + | |
| 52 | + return(realpeak); | |
| 53 | +} | |
| 54 | + | |
| 55 | +float interpolate3phase(float *buf, const int peakindex) | |
| 56 | +{ | |
| 57 | + float a = buf[peakindex-1]; | |
| 58 | + float b = buf[peakindex]; | |
| 59 | + float c = buf[peakindex+1]; | |
| 60 | + float fraction; | |
| 61 | + | |
| 62 | + fraction = ((float)0.5 * (c - a)) / ((float)2. * b - a - c); | |
| 63 | + | |
| 64 | + return(fraction); | |
| 65 | +} | |
| 66 | + | |
| 67 | +// alternative implementation for abs() | |
| 68 | +// REQUIRES: 32 bit integers | |
| 69 | +int fastabs_int(int in){ | |
| 70 | + unsigned int r; | |
| 71 | + int const mask = in >> 31; | |
| 72 | + | |
| 73 | + r = (in ^ mask) - mask; | |
| 74 | + | |
| 75 | + return (r); | |
| 76 | +} | |
| 77 | + | |
| 78 | +// alternative implementation for abs() | |
| 79 | +// REQUIRES: 32 bit floats | |
| 80 | +float fastabsf(float f) | |
| 81 | +{ | |
| 82 | + union | |
| 83 | + { | |
| 84 | + float f; | |
| 85 | + unsigned int ui; | |
| 86 | + }alias; | |
| 87 | + | |
| 88 | + alias.f = f; | |
| 89 | + alias.ui &= 0x7fffffff; | |
| 90 | + return alias.f; | |
| 91 | +} | |
| 92 | + | |
| 93 | +double fastexp(double x) { | |
| 94 | + x = 1.0 + (x * 0.0009765625); | |
| 95 | + x *= x; x *= x; x *= x; x *= x; | |
| 96 | + x *= x; x *= x; x *= x; x *= x; | |
| 97 | + x *= x; x *= x; | |
| 98 | + return x; | |
| 99 | +} | |
| 100 | + | |
| 101 | +float fastexpf(float x) { | |
| 102 | + x = 1.0f + (x * 0.0009765625f); | |
| 103 | + x *= x; x *= x; x *= x; x *= x; | |
| 104 | + x *= x; x *= x; x *= x; x *= x; | |
| 105 | + x *= x; x *= x; | |
| 106 | + return x; | |
| 107 | +} | |
| 108 | + | |
| 109 | +double fasterexp(double x) { | |
| 110 | + x = 1.0 + (x * 0.00390625); | |
| 111 | + x *= x; x *= x; x *= x; x *= x; | |
| 112 | + x *= x; x *= x; x *= x; x *= x; | |
| 113 | + return x; | |
| 114 | +} | |
| 115 | + | |
| 116 | +float fasterexpf(float x) { | |
| 117 | + x = 1.0f + (x * 0.00390625f); | |
| 118 | + x *= x; x *= x; x *= x; x *= x; | |
| 119 | + x *= x; x *= x; x *= x; x *= x; | |
| 120 | + return x; | |
| 121 | +} | |
| 122 | + | |
| 123 | +// fast floating-point exp2 function taken from Robert Bristow Johnson's | |
| 124 | +// post in the music-dsp list on Date: Tue, 02 Sep 2014 16:50:11 -0400 | |
| 125 | +float fastexp2f(float x) | |
| 126 | +{ | |
| 127 | + if (x >= -127.0) | |
| 128 | + { | |
| 129 | + float accumulator, xPower; | |
| 130 | + union {float f; int32_t i;} xBits; | |
| 131 | + | |
| 132 | + xBits.i = (int32_t)(x + 4096.0f) - 4096L; /* integer part */ | |
| 133 | + x -= (float)(xBits.i); /* fractional part */ | |
| 134 | + | |
| 135 | + accumulator = 1.0f + 0.69303212081966f*x; | |
| 136 | + xPower = x*x; | |
| 137 | + accumulator += 0.24137976293709f*xPower; | |
| 138 | + xPower *= x; | |
| 139 | + accumulator += 0.05203236900844f*xPower; | |
| 140 | + xPower *= x; | |
| 141 | + accumulator += 0.01355574723481f*xPower; | |
| 142 | + | |
| 143 | + xBits.i += 127; /* bias integer part */ | |
| 144 | + xBits.i<<= 23; /* move biased int part into exponent bits */ | |
| 145 | + | |
| 146 | + return accumulator * xBits.f; | |
| 147 | + } | |
| 148 | + else | |
| 149 | + { | |
| 150 | + return 0.0f; | |
| 151 | + } | |
| 152 | +} | |
| 153 | + | |
| 154 | + | |
| 155 | +float fastPowf(float a, float b) { | |
| 156 | + union | |
| 157 | + { | |
| 158 | + float d; int x; | |
| 159 | + } | |
| 160 | + u = { a }; | |
| 161 | + | |
| 162 | + u.x = (int)(b * (u.x - 1064866805) + 1064866805); | |
| 163 | + return u.d; | |
| 164 | +} | |
| 165 | + | |
| 166 | + | |
| 167 | +double fastPow(double a, double b) { | |
| 168 | + union { | |
| 169 | + double d; | |
| 170 | + int x[2]; | |
| 171 | + } u = { a }; | |
| 172 | + | |
| 173 | + u.x[1] = (int)(b * (u.x[1] - 1072632447) + 1072632447); | |
| 174 | + | |
| 175 | + u.x[0] = 0; | |
| 176 | + return u.d; | |
| 177 | +} | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | +/* | |
| 184 | + you pass in a float array to get back two indexes representing the volumes of the left (index 0) and right (index 1) channels | |
| 185 | + when t is -1, volumes[0] = 0, volumes[1] = 1 | |
| 186 | + when t = 0, volumes[0] = 0.707, volumes[1] = 0.707 (equal-power cross fade) | |
| 187 | + when t = 1, volumes[0] = 1, volumes[1] = 0 | |
| 188 | + */ | |
| 189 | + | |
| 190 | +void LEAF_crossfade(float fade, float* volumes) { | |
| 191 | + volumes[0] = sqrtf(0.5f * (1.0f + fade)); | |
| 192 | + volumes[1] = sqrtf(0.5f * (1.0f - fade)); | |
| 193 | +} | |
| 194 | + | |
| 195 | +// dope af | |
| 196 | +float LEAF_chebyshevT(float in, int n){ | |
| 197 | + if (n == 0) return 1; | |
| 198 | + else if (n == 1) return in; | |
| 199 | + else return 2.0f * in * LEAF_chebyshevT(in, n-1) - LEAF_chebyshevT(in, n-2); | |
| 200 | +} | |
| 201 | + | |
| 202 | +#if !(_WIN32 || _WIN64) | |
| 203 | +float LEAF_CompoundChebyshevT(float in, int n, float* amps){ | |
| 204 | + float T[n+1]; | |
| 205 | + T[0] = 1.0f; | |
| 206 | + T[1] = in; | |
| 207 | + for (int i = 2; i <= n; ++i) | |
| 208 | + T[i] = 2*in*T[i-1] - T[i-2]; | |
| 209 | + float out = 0; | |
| 210 | + float amp = 0; | |
| 211 | + for (int i = 0; i < n; ++i){ | |
| 212 | + out += amps[i]*T[i+1]; | |
| 213 | + amp += amps[i]; | |
| 214 | + } | |
| 215 | + return out / amp ; | |
| 216 | +} | |
| 217 | +#endif | |
| 218 | + | |
| 219 | +float LEAF_frequencyToMidi(float f) | |
| 220 | +{ | |
| 221 | + return (69.0f + 12.0f * log2f(f * INV_440)); | |
| 222 | +} | |
| 223 | + | |
| 224 | +// Jones shaper | |
| 225 | +float LEAF_shaper(float input, float m_drive) | |
| 226 | +{ | |
| 227 | + float fx = input * 2.0f; // prescale | |
| 228 | + float w, c, xc, xc2, xc4; | |
| 229 | + | |
| 230 | + xc = LEAF_clip(-SQRT8, fx, SQRT8); | |
| 231 | + xc2 = xc*xc; | |
| 232 | + c = 0.5f*fx*(3.0f - (xc2)); | |
| 233 | + xc4 = xc2 * xc2; | |
| 234 | + w = (1.0f - xc2*0.25f + xc4*0.015625f) * WSCALE; | |
| 235 | + float shaperOut = w*(c+ 0.05f*xc2)*(m_drive + 0.75f); | |
| 236 | + shaperOut *= 0.5f; // post_scale | |
| 237 | + return shaperOut; | |
| 238 | +} | |
| 239 | + | |
| 240 | +// round input to nearest rnd | |
| 241 | +float LEAF_round (float input, float rnd) | |
| 242 | +{ | |
| 243 | + rnd = fabsf(rnd); | |
| 244 | + | |
| 245 | + if (rnd <= 0.0000001f) return input; | |
| 246 | + | |
| 247 | + float scale = 1.f / rnd; | |
| 248 | + | |
| 249 | + return roundf(input * scale) / scale; | |
| 250 | +} | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | +float LEAF_bitwise_xor(float input, uint32_t op) | |
| 255 | +{ | |
| 256 | + union unholy_t unholy; | |
| 257 | + unholy.f = input; | |
| 258 | + unholy.i = (unholy.i ^ op); | |
| 259 | + | |
| 260 | + return unholy.f; | |
| 261 | +} | |
| 262 | + | |
| 263 | +float LEAF_reedTable(float input, float offset, float slope) | |
| 264 | +{ | |
| 265 | + float output = offset + (slope * input); | |
| 266 | + if ( output > 1.0f) output = 1.0f; | |
| 267 | + if ( output < -1.0f) output = -1.0f; | |
| 268 | + return output; | |
| 269 | +} | |
| 270 | + | |
| 271 | +float LEAF_softClip(float val, float thresh) | |
| 272 | +{ | |
| 273 | + float x; | |
| 274 | + | |
| 275 | + if(val > thresh) | |
| 276 | + { | |
| 277 | + x = thresh / val; | |
| 278 | + return (1.0f - x) * (1.0f - thresh) + thresh; | |
| 279 | + } | |
| 280 | + else if(val < -thresh) | |
| 281 | + { | |
| 282 | + x = -thresh / val; | |
| 283 | + return -((1.0f - x) * (1.0f - thresh) + thresh); | |
| 284 | + } | |
| 285 | + else | |
| 286 | + { | |
| 287 | + return val; | |
| 288 | + } | |
| 289 | +} | |
| 290 | + | |
| 291 | +float LEAF_clip(float min, float val, float max) | |
| 292 | +{ | |
| 293 | + float tempmin = min; | |
| 294 | + float tempmax = max; | |
| 295 | + if (min > max) | |
| 296 | + { | |
| 297 | + tempmin = max; | |
| 298 | + tempmax = min; | |
| 299 | + } | |
| 300 | + if (val < tempmin) { | |
| 301 | + return tempmin; | |
| 302 | + } else if (val > tempmax) { | |
| 303 | + return tempmax; | |
| 304 | + } else { | |
| 305 | + return val; | |
| 306 | + } | |
| 307 | +} | |
| 308 | + | |
| 309 | +int LEAF_clipInt(int min, int val, int max) | |
| 310 | +{ | |
| 311 | + int tempmin = min; | |
| 312 | + int tempmax = max; | |
| 313 | + if (min > max) | |
| 314 | + { | |
| 315 | + tempmin = max; | |
| 316 | + tempmax = min; | |
| 317 | + } | |
| 318 | + if (val < tempmin) { | |
| 319 | + return tempmin; | |
| 320 | + } else if (val > tempmax) { | |
| 321 | + return tempmax; | |
| 322 | + } else { | |
| 323 | + return val; | |
| 324 | + } | |
| 325 | +} | |
| 326 | + | |
| 327 | +oBool LEAF_isPrime(uint64_t number ) | |
| 328 | +{ | |
| 329 | + if ( number == 2 ) return OTRUE; | |
| 330 | + if ( number & 1 ) { | |
| 331 | + for ( int i=3; i<(int)sqrt((double)number)+1; i+=2 ) | |
| 332 | + if ( (number % i) == 0 ) return OFALSE; | |
| 333 | + return OTRUE; // prime | |
| 334 | + } | |
| 335 | + else return OFALSE; // even | |
| 336 | +} | |
| 337 | + | |
| 338 | +// Adapted from MusicDSP: http://www.musicdsp.org/showone.php?id=238 | |
| 339 | +float LEAF_tanh(float x) | |
| 340 | +{ | |
| 341 | + | |
| 342 | + if( x < -3.0f ) | |
| 343 | + return -1.0f; | |
| 344 | + else if( x > 3.0f ) | |
| 345 | + return 1.0f; | |
| 346 | + else | |
| 347 | + return x * ( 27.0f + x * x ) / ( 27.0f + 9.0f * x * x ); | |
| 348 | +} | |
| 349 | + | |
| 350 | + | |
| 351 | +void LEAF_generate_sine(float* buffer, int size) | |
| 352 | +{ | |
| 353 | + float phase; | |
| 354 | + for (int i = 0; i < size; i++) | |
| 355 | + { | |
| 356 | + phase = (float) i / (float) size; | |
| 357 | + buffer[i] = sinf(phase * TWO_PI); | |
| 358 | + } | |
| 359 | +} | |
| 360 | + | |
| 361 | +void LEAF_generate_sawtooth(float* buffer, float basefreq, int size) | |
| 362 | +{ | |
| 363 | + int harmonic = 1; | |
| 364 | + float phase = 0.0f; | |
| 365 | + float freq = harmonic * basefreq; | |
| 366 | + float amp; | |
| 367 | + | |
| 368 | + while (freq < (leaf.sampleRate * 0.5)) | |
| 369 | + { | |
| 370 | + amp = 1.0f / harmonic; | |
| 371 | + for (int i = 0; i < size; i++) | |
| 372 | + { | |
| 373 | + phase = (float) i / (float) size; | |
| 374 | + buffer[i] += (amp * sinf(harmonic * phase * TWO_PI)); | |
| 375 | + } | |
| 376 | + | |
| 377 | + harmonic++; | |
| 378 | + freq = harmonic * basefreq; | |
| 379 | + } | |
| 380 | +} | |
| 381 | + | |
| 382 | + | |
| 383 | +void LEAF_generate_triangle(float* buffer, float basefreq, int size) | |
| 384 | +{ | |
| 385 | + int harmonic = 1; | |
| 386 | + float phase = 0.0f; | |
| 387 | + float freq = harmonic * basefreq; | |
| 388 | + float amp = 1.0f; | |
| 389 | + | |
| 390 | + int count = 0; | |
| 391 | + float mult = 1.0f; | |
| 392 | + | |
| 393 | + while (freq < (leaf.sampleRate * 0.5)) | |
| 394 | + { | |
| 395 | + amp = 1.0f / (float)(harmonic * harmonic); | |
| 396 | + | |
| 397 | + if (count % 2) mult = -1.0f; | |
| 398 | + else mult = 1.0f; | |
| 399 | + | |
| 400 | + for (int i = 0; i < size; i++) | |
| 401 | + { | |
| 402 | + phase = (float) i / (float) size; | |
| 403 | + buffer[i] += (mult * amp * sinf(harmonic * phase * TWO_PI)); | |
| 404 | + } | |
| 405 | + | |
| 406 | + count++; | |
| 407 | + harmonic += 2; | |
| 408 | + freq = harmonic * basefreq; | |
| 409 | + } | |
| 410 | +} | |
| 411 | + | |
| 412 | +void LEAF_generate_square(float* buffer, float basefreq, int size) | |
| 413 | +{ | |
| 414 | + int harmonic = 1; | |
| 415 | + float phase = 0.0f; | |
| 416 | + float freq = harmonic * basefreq; | |
| 417 | + float amp = 1.0f; | |
| 418 | + | |
| 419 | + while (freq < (leaf.sampleRate * 0.5)) | |
| 420 | + { | |
| 421 | + amp = 1.0f / (float)(harmonic); | |
| 422 | + | |
| 423 | + for (int i = 0; i < size; i++) | |
| 424 | + { | |
| 425 | + phase = (float) i / (float) size; | |
| 426 | + buffer[i] += (amp * sinf(harmonic * phase * TWO_PI)); | |
| 427 | + } | |
| 428 | + | |
| 429 | + harmonic += 2; | |
| 430 | + freq = harmonic * basefreq; | |
| 431 | + } | |
| 432 | +} | |
| 433 | + | |
| 434 | + | |
| 435 | +//0.001 base gives a good curve that goes from 1 to near zero | |
| 436 | +void LEAF_generate_exp(float* buffer, float base, float start, float end, float offset, int size) | |
| 437 | +{ | |
| 438 | + float increment = (end - start) / (float)size; | |
| 439 | + float x = start; | |
| 440 | + for (int i = 0; i < size; i++) | |
| 441 | + { | |
| 442 | + buffer[i] = powf(base, x) + offset; | |
| 443 | + x += increment; | |
| 444 | + } | |
| 445 | +} | |
| 446 | + | |
| 447 | + | |
| 448 | +// http://www.martin-finke.de/blog/articles/audio-plugins-018-polyblep-oscillator/ | |
| 449 | +// http://www.kvraudio.com/forum/viewtopic.php?t=375517 | |
| 450 | +// t = phase, dt = inc, assuming 0-1 phase | |
| 451 | +// assumes discontinuity at 0, so offset inputs as needed | |
| 452 | +float LEAF_poly_blep(float t, float dt) | |
| 453 | +{ | |
| 454 | + // 0 <= t < 1 | |
| 455 | + if (t < dt) { | |
| 456 | + t /= dt; | |
| 457 | + return t+t - t*t - 1.0f; | |
| 458 | + } | |
| 459 | + // -1 < t < 0 | |
| 460 | + else if (t > 1.0f - dt) { | |
| 461 | + t = (t - 1.0f) / dt; | |
| 462 | + return t*t + t+t + 1.0f; | |
| 463 | + } | |
| 464 | + // 0 otherwise | |
| 465 | + else return 0.0f; | |
| 466 | + | |
| 467 | +// | |
| 468 | +// float y = 0.0f; | |
| 469 | +// if (t < 2.0f * dt) | |
| 470 | +// { | |
| 471 | +// float x = t / dt; | |
| 472 | +// float u = 2.0f - x; | |
| 473 | +// u *= u; | |
| 474 | +// u *= u; | |
| 475 | +// y += u; | |
| 476 | +// if (t < dt) { | |
| 477 | +// float v = 1.0f - x; | |
| 478 | +// v *= v; | |
| 479 | +// v *= v; | |
| 480 | +// y -= 4.0f * v; | |
| 481 | +// } | |
| 482 | +// } | |
| 483 | +// else if (t > 1.0f - (2.0f * dt)) | |
| 484 | +// { | |
| 485 | +// float x = (t - 1.0f) / dt; | |
| 486 | +// float u = 2.0f - x; | |
| 487 | +// u *= u; | |
| 488 | +// u *= u; | |
| 489 | +// y += u; | |
| 490 | +// if (t > 1.0f - dt) { | |
| 491 | +// float v = 1.0f - x; | |
| 492 | +// v *= v; | |
| 493 | +// v *= v; | |
| 494 | +// y += 4.0f * v; | |
| 495 | +// } | |
| 496 | +// } | |
| 497 | +// return y / 12.0f; | |
| 498 | +} | |
| 499 | + | |
| 500 | + | |
| 501 | +//----------------------------------------------------------------------------- | |
| 502 | +// name: mtof() | |
| 503 | +// desc: midi to freq, from PD source | |
| 504 | +//----------------------------------------------------------------------------- | |
| 505 | +float LEAF_midiToFrequency(float f) | |
| 506 | +{ | |
| 507 | + if( f <= -1500.0f ) return (0); | |
| 508 | + else if( f > 1499.0f ) return (LEAF_midiToFrequency(1499.0f)); | |
| 509 | + else return ( powf(2.0f, (f - 69.0f) * 0.083333333333333f) * 440.0f ); | |
| 510 | +} | |
| 511 | + | |
| 512 | + | |
| 513 | +// alpha, [0.0, 1.0] | |
| 514 | +float LEAF_interpolate_hermite (float A, float B, float C, float D, float alpha) | |
| 515 | +{ | |
| 516 | + alpha = LEAF_clip(0.0f, alpha, 1.0f); | |
| 517 | + | |
| 518 | + float a = -A*0.5f + (3.0f*B)*0.5f - (3.0f*C)*0.5f + D*0.5f; | |
| 519 | + float b = A - (5.0f*B)*0.5f + 2.0f*C - D * 0.5f; | |
| 520 | + float c = -A*0.5f + C*0.5f; | |
| 521 | + float d = B; | |
| 522 | + | |
| 523 | + return a*alpha*alpha*alpha + b*alpha*alpha + c*alpha + d; | |
| 524 | +} | |
| 525 | + | |
| 526 | + | |
| 527 | +// from http://www.musicdsp.org/archive.php?classid=5#93 | |
| 528 | +//xx is alpha (fractional part of sample value) | |
| 529 | +//grabbed this from Tom Erbe's Delay pd code | |
| 530 | +float LEAF_interpolate_hermite_x(float yy0, float yy1, float yy2, float yy3, float xx) | |
| 531 | +{ | |
| 532 | + // 4-point, 3rd-order Hermite (x-form) | |
| 533 | + float c0 = yy1; | |
| 534 | + float c1 = 0.5f * (yy2 - yy0); | |
| 535 | + float y0my1 = yy0 - yy1; | |
| 536 | + float c3 = (yy1 - yy2) + 0.5f * (yy3 - y0my1 - yy2); | |
| 537 | + float c2 = y0my1 + c1 - c3; | |
| 538 | + | |
| 539 | + return ((c3 * xx + c2) * xx + c1) * xx + c0; | |
| 540 | +} | |
| 541 | + | |
| 542 | +// alpha, [0.0, 1.0] | |
| 543 | +float LEAF_interpolation_linear (float A, float B, float alpha) | |
| 544 | +{ | |
| 545 | + alpha = LEAF_clip(0.0f, alpha, 1.0f); | |
| 546 | + | |
| 547 | + float omAlpha = 1.0f - alpha; | |
| 548 | + | |
| 549 | + // First 1/2 of interpolation | |
| 550 | + float out = A * omAlpha; | |
| 551 | + | |
| 552 | + out += B * alpha; | |
| 553 | + | |
| 554 | + return out; | |
| 555 | +} | |
| 556 | + | |
| 557 | +#define LOGTEN 2.302585092994 | |
| 558 | + | |
| 559 | +float mtof(float f) | |
| 560 | +{ | |
| 561 | + if (f <= -1500.0f) return(0); | |
| 562 | + else if (f > 1499.0f) return(mtof(1499.0f)); | |
| 563 | + else return (8.17579891564f * expf(0.0577622650f * f)); | |
| 564 | +} | |
| 565 | + | |
| 566 | +float fast_mtof(float f) | |
| 567 | +{ | |
| 568 | + return (8.17579891564f * fastexpf(0.0577622650f * f)); | |
| 569 | +} | |
| 570 | + | |
| 571 | +float faster_mtof(float f) | |
| 572 | +{ | |
| 573 | + return (8.17579891564f * fastexpf(0.0577622650f * f)); | |
| 574 | +} | |
| 575 | + | |
| 576 | +float ftom(float f) | |
| 577 | +{ | |
| 578 | + return (f > 0 ? 17.3123405046f * logf(.12231220585f * f) : -1500.0f); | |
| 579 | +} | |
| 580 | + | |
| 581 | +float powtodb(float f) | |
| 582 | +{ | |
| 583 | + if (f <= 0) return (0); | |
| 584 | + else | |
| 585 | + { | |
| 586 | + float val = 100.0f + 10.0f/LOGTEN * logf(f); | |
| 587 | + return (val < 0.0f ? 0.0f : val); | |
| 588 | + } | |
| 589 | +} | |
| 590 | + | |
| 591 | +float rmstodb(float f) | |
| 592 | +{ | |
| 593 | + if (f <= 0) return (0); | |
| 594 | + else | |
| 595 | + { | |
| 596 | + float val = 100 + 20.f/LOGTEN * log(f); | |
| 597 | + return (val < 0 ? 0 : val); | |
| 598 | + } | |
| 599 | +} | |
| 600 | + | |
| 601 | +float dbtopow(float f) | |
| 602 | +{ | |
| 603 | + if (f <= 0) | |
| 604 | + return(0); | |
| 605 | + else | |
| 606 | + { | |
| 607 | + if (f > 870.0f) | |
| 608 | + f = 870.0f; | |
| 609 | + return (expf((LOGTEN * 0.1f) * (f-100.0f))); | |
| 610 | + } | |
| 611 | +} | |
| 612 | + | |
| 613 | +float dbtorms(float f) | |
| 614 | +{ | |
| 615 | + if (f <= 0) | |
| 616 | + return(0); | |
| 617 | + else | |
| 618 | + { | |
| 619 | + if (f > 485.0f) | |
| 620 | + f = 485.0f; | |
| 621 | + } | |
| 622 | + return (expf((LOGTEN * 0.05f) * (f-100.0f))); | |
| 623 | +} | |
| 624 | + | |
| 625 | + | |
| 626 | +float atodb(float a) | |
| 627 | +{ | |
| 628 | + return 20.0f*log10f(a); | |
| 629 | +} | |
| 630 | + | |
| 631 | +float dbtoa(float db) | |
| 632 | +{ | |
| 633 | + return powf(10.0f, db * 0.05f); | |
| 634 | + //return expf(0.115129254649702f * db); //faster version from http://openaudio.blogspot.com/2017/02/faster-log10-and-pow.html | |
| 635 | +} | |
| 636 | + | |
| 637 | + | |
| 638 | +float fastdbtoa(float db) | |
| 639 | +{ | |
| 640 | + //return powf(10.0f, db * 0.05f); | |
| 641 | + return expf(0.115129254649702f * db); //faster version from http://openaudio.blogspot.com/2017/02/faster-log10-and-pow.html | |
| 642 | +} | |
| 643 | + | |
| 644 | + | |
| 645 | +float maximum (float num1, float num2) | |
| 646 | +{ | |
| 647 | + return (num1 > num2 ) ? num1 : num2; | |
| 648 | +} | |
| 649 | + | |
| 650 | +float minimum (float num1, float num2) | |
| 651 | +{ | |
| 652 | + return (num1 < num2 ) ? num1 : num2; | |
| 653 | +} | |
| 654 | + | |
| 655 | + | |
| @@ -0,0 +1,412 @@ | ||
| 1 | + | |
| 2 | +/** mpool source significantly modified by Mike Mulshine, Jeff Snyder, et al., Princeton University Music Department **/ | |
| 3 | + | |
| 4 | +/** | |
| 5 | + In short, mpool is distributed under so called "BSD license", | |
| 6 | + | |
| 7 | + Copyright (c) 2009-2010 Tatsuhiko Kubo <cubicdaiya@gmail.com> | |
| 8 | + All rights reserved. | |
| 9 | + | |
| 10 | + Redistribution and use in source and binary forms, with or without modification, | |
| 11 | + are permitted provided that the following conditions are met: | |
| 12 | + | |
| 13 | + * Redistributions of source code must retain the above copyright notice, | |
| 14 | + this list of conditions and the following disclaimer. | |
| 15 | + | |
| 16 | + * Redistributions in binary form must reproduce the above copyright notice, | |
| 17 | + this list of conditions and the following disclaimer in the documentation | |
| 18 | + and/or other materials provided with the distribution. | |
| 19 | + | |
| 20 | + * Neither the name of the authors nor the names of its contributors | |
| 21 | + may be used to endorse or promote products derived from this software | |
| 22 | + without specific prior written permission. | |
| 23 | + | |
| 24 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 25 | + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 26 | + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 27 | + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 28 | + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 29 | + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |
| 30 | + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 31 | + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
| 32 | + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 33 | + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 34 | + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 35 | + */ | |
| 36 | + | |
| 37 | +/* written with C99 style */ | |
| 38 | + | |
| 39 | +#if _WIN32 || _WIN64 | |
| 40 | + | |
| 41 | +#include "..\Inc\leaf-mempool.h" | |
| 42 | +#include "..\leaf.h" | |
| 43 | + | |
| 44 | +#else | |
| 45 | + | |
| 46 | +#include "../Inc/leaf-mempool.h" | |
| 47 | +#include "../leaf.h" | |
| 48 | + | |
| 49 | +#endif | |
| 50 | + | |
| 51 | +/** | |
| 52 | + * private function | |
| 53 | + */ | |
| 54 | +static inline size_t mpool_align(size_t size); | |
| 55 | +static inline mpool_node_t* create_node(char* block_location, mpool_node_t* next, mpool_node_t* prev, size_t size); | |
| 56 | +static inline void delink_node(mpool_node_t* node); | |
| 57 | + | |
| 58 | +/** | |
| 59 | + * create memory pool | |
| 60 | + */ | |
| 61 | +void mpool_create (char* memory, size_t size, _tMempool* pool) | |
| 62 | +{ | |
| 63 | + leaf.header_size = mpool_align(sizeof(mpool_node_t)); | |
| 64 | + | |
| 65 | + pool->mpool = (char*)memory; | |
| 66 | + pool->usize = 0; | |
| 67 | + pool->msize = size; | |
| 68 | + | |
| 69 | + pool->head = create_node(pool->mpool, NULL, NULL, pool->msize-leaf.header_size); | |
| 70 | + | |
| 71 | + /* | |
| 72 | + for (int i = 0; i < pool->head->size; i++) | |
| 73 | + { | |
| 74 | + memory[i+leaf.header_size]=0; | |
| 75 | + } | |
| 76 | + */ | |
| 77 | + //is zeroing out the memory necessary? This takes a long time on large pools - JS | |
| 78 | +} | |
| 79 | + | |
| 80 | +void leaf_pool_init(char* memory, size_t size) | |
| 81 | +{ | |
| 82 | + mpool_create(memory, size, &leaf._mempool); | |
| 83 | + | |
| 84 | + leaf.mempool = &leaf._mempool; | |
| 85 | +} | |
| 86 | + | |
| 87 | +/** | |
| 88 | + * allocate memory from memory pool | |
| 89 | + */ | |
| 90 | +char* mpool_alloc(size_t asize, _tMempool* pool) | |
| 91 | +{ | |
| 92 | + // If the head is NULL, the mempool is full | |
| 93 | + if (pool->head == NULL) | |
| 94 | + { | |
| 95 | + leaf_mempool_overrun(); | |
| 96 | + return NULL; | |
| 97 | + } | |
| 98 | + | |
| 99 | + // Should we alloc the first block large enough or check all blocks and pick the one closest in size? | |
| 100 | + size_t size_to_alloc = mpool_align(asize); | |
| 101 | + mpool_node_t* node_to_alloc = pool->head; | |
| 102 | + | |
| 103 | + // Traverse the free list for a large enough block | |
| 104 | + while (node_to_alloc->size < size_to_alloc) | |
| 105 | + { | |
| 106 | + node_to_alloc = node_to_alloc->next; | |
| 107 | + | |
| 108 | + // If we reach the end of the free list, there | |
| 109 | + // are no blocks large enough, return NULL | |
| 110 | + if (node_to_alloc == NULL) | |
| 111 | + { | |
| 112 | + leaf_mempool_overrun(); | |
| 113 | + return NULL; | |
| 114 | + } | |
| 115 | + } | |
| 116 | + | |
| 117 | + // Create a new node after the node to be allocated if there is enough space | |
| 118 | + mpool_node_t* new_node; | |
| 119 | + size_t leftover = node_to_alloc->size - size_to_alloc; | |
| 120 | + node_to_alloc->size = size_to_alloc; | |
| 121 | + if (leftover > leaf.header_size) | |
| 122 | + { | |
| 123 | + long offset = (char*) node_to_alloc - (char*) pool->mpool; | |
| 124 | + offset += leaf.header_size + node_to_alloc->size; | |
| 125 | + new_node = create_node(&pool->mpool[offset], | |
| 126 | + node_to_alloc->next, | |
| 127 | + node_to_alloc->prev, | |
| 128 | + leftover - leaf.header_size); | |
| 129 | + } | |
| 130 | + else | |
| 131 | + { | |
| 132 | + // Add any leftover space to the allocated node to avoid fragmentation | |
| 133 | + node_to_alloc->size += leftover; | |
| 134 | + | |
| 135 | + new_node = node_to_alloc->next; | |
| 136 | + } | |
| 137 | + | |
| 138 | + // Update the head if we are allocating the first node of the free list | |
| 139 | + // The head will be NULL if there is no space left | |
| 140 | + if (pool->head == node_to_alloc) | |
| 141 | + { | |
| 142 | + pool->head = new_node; | |
| 143 | + } | |
| 144 | + | |
| 145 | + // Remove the allocated node from the free list | |
| 146 | + delink_node(node_to_alloc); | |
| 147 | + | |
| 148 | + pool->usize += leaf.header_size + node_to_alloc->size; | |
| 149 | + | |
| 150 | + if (leaf.clearOnAllocation > 0) | |
| 151 | + { | |
| 152 | + char* new_pool = (char*)node_to_alloc->pool; | |
| 153 | + for (int i = 0; i < node_to_alloc->size; i++) new_pool[i] = 0; | |
| 154 | + } | |
| 155 | + | |
| 156 | + // Return the pool of the allocated node; | |
| 157 | + return node_to_alloc->pool; | |
| 158 | +} | |
| 159 | + | |
| 160 | + | |
| 161 | +/** | |
| 162 | + * allocate memory from memory pool and also clear that memory to be blank | |
| 163 | + */ | |
| 164 | +char* mpool_calloc(size_t asize, _tMempool* pool) | |
| 165 | +{ | |
| 166 | + // If the head is NULL, the mempool is full | |
| 167 | + if (pool->head == NULL) | |
| 168 | + { | |
| 169 | + leaf_mempool_overrun(); | |
| 170 | + return NULL; | |
| 171 | + } | |
| 172 | + | |
| 173 | + // Should we alloc the first block large enough or check all blocks and pick the one closest in size? | |
| 174 | + size_t size_to_alloc = mpool_align(asize); | |
| 175 | + mpool_node_t* node_to_alloc = pool->head; | |
| 176 | + | |
| 177 | + // Traverse the free list for a large enough block | |
| 178 | + while (node_to_alloc->size < size_to_alloc) | |
| 179 | + { | |
| 180 | + node_to_alloc = node_to_alloc->next; | |
| 181 | + | |
| 182 | + // If we reach the end of the free list, there | |
| 183 | + // are no blocks large enough, return NULL | |
| 184 | + if (node_to_alloc == NULL) | |
| 185 | + { | |
| 186 | + leaf_mempool_overrun(); | |
| 187 | + return NULL; | |
| 188 | + } | |
| 189 | + } | |
| 190 | + | |
| 191 | + // Create a new node after the node to be allocated if there is enough space | |
| 192 | + mpool_node_t* new_node; | |
| 193 | + size_t leftover = node_to_alloc->size - size_to_alloc; | |
| 194 | + node_to_alloc->size = size_to_alloc; | |
| 195 | + if (leftover > leaf.header_size) | |
| 196 | + { | |
| 197 | + long offset = (char*) node_to_alloc - (char*) pool->mpool; | |
| 198 | + offset += leaf.header_size + node_to_alloc->size; | |
| 199 | + new_node = create_node(&pool->mpool[offset], | |
| 200 | + node_to_alloc->next, | |
| 201 | + node_to_alloc->prev, | |
| 202 | + leftover - leaf.header_size); | |
| 203 | + } | |
| 204 | + else | |
| 205 | + { | |
| 206 | + // Add any leftover space to the allocated node to avoid fragmentation | |
| 207 | + node_to_alloc->size += leftover; | |
| 208 | + | |
| 209 | + new_node = node_to_alloc->next; | |
| 210 | + } | |
| 211 | + | |
| 212 | + // Update the head if we are allocating the first node of the free list | |
| 213 | + // The head will be NULL if there is no space left | |
| 214 | + if (pool->head == node_to_alloc) | |
| 215 | + { | |
| 216 | + pool->head = new_node; | |
| 217 | + } | |
| 218 | + | |
| 219 | + // Remove the allocated node from the free list | |
| 220 | + delink_node(node_to_alloc); | |
| 221 | + | |
| 222 | + pool->usize += leaf.header_size + node_to_alloc->size; | |
| 223 | + // Format the new pool | |
| 224 | + char* new_pool = (char*)node_to_alloc->pool; | |
| 225 | + for (int i = 0; i < node_to_alloc->size; i++) new_pool[i] = 0; | |
| 226 | + // Return the pool of the allocated node; | |
| 227 | + return node_to_alloc->pool; | |
| 228 | +} | |
| 229 | + | |
| 230 | +char* leaf_alloc(size_t size) | |
| 231 | +{ | |
| 232 | + //printf("alloc %i\n", size); | |
| 233 | + char* block = mpool_alloc(size, &leaf._mempool); | |
| 234 | + | |
| 235 | + if (block == NULL) leaf_mempool_overrun(); | |
| 236 | + | |
| 237 | + return block; | |
| 238 | +} | |
| 239 | + | |
| 240 | +char* leaf_calloc(size_t size) | |
| 241 | +{ | |
| 242 | + //printf("alloc %i\n", size); | |
| 243 | + char* block = mpool_calloc(size, &leaf._mempool); | |
| 244 | + | |
| 245 | + if (block == NULL) leaf_mempool_overrun(); | |
| 246 | + | |
| 247 | + | |
| 248 | + return block; | |
| 249 | +} | |
| 250 | + | |
| 251 | +void mpool_free(char* ptr, _tMempool* pool) | |
| 252 | +{ | |
| 253 | + //if (ptr < pool->mpool || ptr >= pool->mpool + pool->msize) | |
| 254 | + // Get the node at the freed space | |
| 255 | + mpool_node_t* freed_node = (mpool_node_t*) (ptr - leaf.header_size); | |
| 256 | + | |
| 257 | + pool->usize -= leaf.header_size + freed_node->size; | |
| 258 | + | |
| 259 | + // Check each node in the list against the newly freed one to see if it's adjacent in memory | |
| 260 | + mpool_node_t* other_node = pool->head; | |
| 261 | + mpool_node_t* next_node; | |
| 262 | + while (other_node != NULL) | |
| 263 | + { | |
| 264 | + next_node = other_node->next; | |
| 265 | + // Check if a node is directly after the freed node | |
| 266 | + if ((long) freed_node + (leaf.header_size + freed_node->size) == (long) other_node) | |
| 267 | + { | |
| 268 | + // Increase freed node's size | |
| 269 | + freed_node->size += leaf.header_size + other_node->size; | |
| 270 | + // If we are merging with the head, move the head forward | |
| 271 | + if (other_node == pool->head) pool->head = pool->head->next; | |
| 272 | + // Delink the merged node | |
| 273 | + delink_node(other_node); | |
| 274 | + } | |
| 275 | + | |
| 276 | + // Check if a node is directly before the freed node | |
| 277 | + else if ((long) other_node + (leaf.header_size + other_node->size) == (long) freed_node) | |
| 278 | + { | |
| 279 | + // Increase the merging node's size | |
| 280 | + other_node->size += leaf.header_size + freed_node->size; | |
| 281 | + | |
| 282 | + if (other_node != pool->head) | |
| 283 | + { | |
| 284 | + // Delink the merging node | |
| 285 | + delink_node(other_node); | |
| 286 | + // Attach the merging node to the head | |
| 287 | + other_node->next = pool->head; | |
| 288 | + // Merge | |
| 289 | + freed_node = other_node; | |
| 290 | + } | |
| 291 | + else | |
| 292 | + { | |
| 293 | + // If we are merging with the head, move the head forward | |
| 294 | + pool->head = pool->head->next; | |
| 295 | + // Merge | |
| 296 | + freed_node = other_node; | |
| 297 | + } | |
| 298 | + } | |
| 299 | + | |
| 300 | + other_node = next_node; | |
| 301 | + } | |
| 302 | + | |
| 303 | + // Ensure the freed node is attached to the head | |
| 304 | + freed_node->next = pool->head; | |
| 305 | + if (pool->head != NULL) pool->head->prev = freed_node; | |
| 306 | + pool->head = freed_node; | |
| 307 | + | |
| 308 | + // Format the freed pool | |
| 309 | + // char* freed_pool = (char*)freed_node->pool; | |
| 310 | + // for (int i = 0; i < freed_node->size; i++) freed_pool[i] = 0; | |
| 311 | +} | |
| 312 | + | |
| 313 | +void leaf_free(char* ptr) | |
| 314 | +{ | |
| 315 | + mpool_free(ptr, &leaf._mempool); | |
| 316 | +} | |
| 317 | + | |
| 318 | +size_t mpool_get_size(_tMempool* pool) | |
| 319 | +{ | |
| 320 | + return pool->msize; | |
| 321 | +} | |
| 322 | + | |
| 323 | +size_t mpool_get_used(_tMempool* pool) | |
| 324 | +{ | |
| 325 | + return pool->usize; | |
| 326 | +} | |
| 327 | + | |
| 328 | +size_t leaf_pool_get_size(void) | |
| 329 | +{ | |
| 330 | + return mpool_get_size(&leaf._mempool); | |
| 331 | +} | |
| 332 | + | |
| 333 | +size_t leaf_pool_get_used(void) | |
| 334 | +{ | |
| 335 | + return mpool_get_used(&leaf._mempool); | |
| 336 | +} | |
| 337 | + | |
| 338 | +char* leaf_pool_get_pool(void) | |
| 339 | +{ | |
| 340 | + char* buff = leaf._mempool.mpool; | |
| 341 | + | |
| 342 | + return buff; | |
| 343 | +} | |
| 344 | + | |
| 345 | +/** | |
| 346 | + * align byte boundary | |
| 347 | + */ | |
| 348 | +static inline size_t mpool_align(size_t size) { | |
| 349 | + return (size + (MPOOL_ALIGN_SIZE - 1)) & ~(MPOOL_ALIGN_SIZE - 1); | |
| 350 | +} | |
| 351 | + | |
| 352 | +static inline mpool_node_t* create_node(char* block_location, mpool_node_t* next, mpool_node_t* prev, size_t size) | |
| 353 | +{ | |
| 354 | + mpool_node_t* node = (mpool_node_t*)block_location; | |
| 355 | + node->pool = block_location + leaf.header_size; | |
| 356 | + node->next = next; | |
| 357 | + node->prev = prev; | |
| 358 | + node->size = size; | |
| 359 | + | |
| 360 | + return node; | |
| 361 | +} | |
| 362 | + | |
| 363 | +static inline void delink_node(mpool_node_t* node) | |
| 364 | +{ | |
| 365 | + // If there is a node after the node to remove | |
| 366 | + if (node->next != NULL) | |
| 367 | + { | |
| 368 | + // Close the link | |
| 369 | + node->next->prev = node->prev; | |
| 370 | + } | |
| 371 | + // If there is a node before the node to remove | |
| 372 | + if (node->prev != NULL) | |
| 373 | + { | |
| 374 | + // Close the link | |
| 375 | + node->prev->next = node->next; | |
| 376 | + } | |
| 377 | + | |
| 378 | + node->next = NULL; | |
| 379 | + node->prev = NULL; | |
| 380 | +} | |
| 381 | + | |
| 382 | +void leaf_mempool_overrun(void) | |
| 383 | +{ | |
| 384 | + LEAF_error(1); | |
| 385 | + //TODO: we should make a set of real error codes that are in an enum type | |
| 386 | +} | |
| 387 | + | |
| 388 | +void tMempool_init(tMempool* const mp, char* memory, size_t size) | |
| 389 | +{ | |
| 390 | + tMempool_initToPool(mp, memory, size, &leaf.mempool); | |
| 391 | +} | |
| 392 | + | |
| 393 | +void tMempool_free(tMempool* const mp) | |
| 394 | +{ | |
| 395 | + tMempool_freeFromPool(mp, &leaf.mempool); | |
| 396 | +} | |
| 397 | + | |
| 398 | +void tMempool_initToPool (tMempool* const mp, char* memory, size_t size, tMempool* const mem) | |
| 399 | +{ | |
| 400 | + _tMempool* mm = *mem; | |
| 401 | + _tMempool* m = *mp = (_tMempool*) mpool_alloc(sizeof(_tMempool), mm); | |
| 402 | + | |
| 403 | + mpool_create (memory, size, m); | |
| 404 | +} | |
| 405 | + | |
| 406 | +void tMempool_freeFromPool (tMempool* const mp, tMempool* const mem) | |
| 407 | +{ | |
| 408 | + _tMempool* mm = *mem; | |
| 409 | + _tMempool* m = *mp; | |
| 410 | + | |
| 411 | + mpool_free((char*)m, mm); | |
| 412 | +} | |
| @@ -0,0 +1,980 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-midi.c | |
| 4 | + Created: 30 Nov 2018 11:29:16am | |
| 5 | + Author: airship | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-midi.h" | |
| 12 | + | |
| 13 | +#else | |
| 14 | + | |
| 15 | +#include "../Inc/leaf-midi.h" | |
| 16 | + | |
| 17 | +#endif | |
| 18 | + | |
| 19 | +//==================================================================================== | |
| 20 | +/* Stack */ | |
| 21 | +//==================================================================================== | |
| 22 | + | |
| 23 | +void tStack_init(tStack* const stack) | |
| 24 | +{ | |
| 25 | + _tStack* ns = *stack = (_tStack*) leaf_alloc(sizeof(_tStack)); | |
| 26 | + | |
| 27 | + ns->ordered = OFALSE; | |
| 28 | + ns->size = 0; | |
| 29 | + ns->pos = 0; | |
| 30 | + ns->capacity = STACK_SIZE; | |
| 31 | + | |
| 32 | + for (int i = 0; i < STACK_SIZE; i++) ns->data[i] = -1; | |
| 33 | +} | |
| 34 | + | |
| 35 | +void tStack_free(tStack* const stack) | |
| 36 | +{ | |
| 37 | + _tStack* ns = *stack; | |
| 38 | + | |
| 39 | + leaf_free((char*)ns); | |
| 40 | +} | |
| 41 | + | |
| 42 | +void tStack_initToPool (tStack* const stack, tMempool* const mp) | |
| 43 | +{ | |
| 44 | + _tMempool* m = *mp; | |
| 45 | + _tStack* ns = *stack = (_tStack*) mpool_alloc(sizeof(_tStack), m); | |
| 46 | + | |
| 47 | + ns->ordered = OFALSE; | |
| 48 | + ns->size = 0; | |
| 49 | + ns->pos = 0; | |
| 50 | + ns->capacity = STACK_SIZE; | |
| 51 | + | |
| 52 | + for (int i = 0; i < STACK_SIZE; i++) ns->data[i] = -1; | |
| 53 | +} | |
| 54 | + | |
| 55 | +void tStack_freeFromPool (tStack* const stack, tMempool* const mp) | |
| 56 | +{ | |
| 57 | + _tMempool* m = *mp; | |
| 58 | + _tStack* ns = *stack; | |
| 59 | + | |
| 60 | + mpool_free((char*)ns, m); | |
| 61 | +} | |
| 62 | + | |
| 63 | +// If stack contains note, returns index. Else returns -1; | |
| 64 | +int tStack_contains(tStack* const stack, uint16_t noteVal) | |
| 65 | +{ | |
| 66 | + _tStack* ns = *stack; | |
| 67 | + for (int i = 0; i < ns->size; i++) | |
| 68 | + { | |
| 69 | + if (ns->data[i] == noteVal) return i; | |
| 70 | + } | |
| 71 | + return -1; | |
| 72 | +} | |
| 73 | + | |
| 74 | +void tStack_add(tStack* const stack, uint16_t noteVal) | |
| 75 | +{ | |
| 76 | + _tStack* ns = *stack; | |
| 77 | + | |
| 78 | + uint8_t j; | |
| 79 | + | |
| 80 | + int whereToInsert = 0; | |
| 81 | + if (ns->ordered) | |
| 82 | + { | |
| 83 | + for (j = 0; j < ns->size; j++) | |
| 84 | + { | |
| 85 | + if (noteVal > ns->data[j]) | |
| 86 | + { | |
| 87 | + if ((noteVal < ns->data[j+1]) || (ns->data[j+1] == -1)) | |
| 88 | + { | |
| 89 | + whereToInsert = j+1; | |
| 90 | + break; | |
| 91 | + } | |
| 92 | + } | |
| 93 | + } | |
| 94 | + } | |
| 95 | + | |
| 96 | + //first move notes that are already in the stack one position to the right | |
| 97 | + for (j = ns->size; j > whereToInsert; j--) | |
| 98 | + { | |
| 99 | + ns->data[j] = ns->data[(j - 1)]; | |
| 100 | + } | |
| 101 | + | |
| 102 | + //then, insert the new note into the front of the stack | |
| 103 | + ns->data[whereToInsert] = noteVal; | |
| 104 | + | |
| 105 | + ns->size++; | |
| 106 | +} | |
| 107 | + | |
| 108 | +int tStack_addIfNotAlreadyThere(tStack* const stack, uint16_t noteVal) | |
| 109 | +{ | |
| 110 | + _tStack* ns = *stack; | |
| 111 | + | |
| 112 | + uint8_t j; | |
| 113 | + | |
| 114 | + int added = 0; | |
| 115 | + | |
| 116 | + if (tStack_contains(stack, noteVal) == -1) | |
| 117 | + { | |
| 118 | + int whereToInsert = 0; | |
| 119 | + if (ns->ordered) | |
| 120 | + { | |
| 121 | + for (j = 0; j < ns->size; j++) | |
| 122 | + { | |
| 123 | + if (noteVal > ns->data[j]) | |
| 124 | + { | |
| 125 | + if ((noteVal < ns->data[j+1]) || (ns->data[j+1] == -1)) | |
| 126 | + { | |
| 127 | + whereToInsert = j+1; | |
| 128 | + break; | |
| 129 | + } | |
| 130 | + } | |
| 131 | + } | |
| 132 | + } | |
| 133 | + | |
| 134 | + //first move notes that are already in the stack one position to the right | |
| 135 | + for (j = ns->size; j > whereToInsert; j--) | |
| 136 | + { | |
| 137 | + ns->data[j] = ns->data[(j - 1)]; | |
| 138 | + } | |
| 139 | + | |
| 140 | + //then, insert the new note into the front of the stack | |
| 141 | + ns->data[whereToInsert] = noteVal; | |
| 142 | + | |
| 143 | + ns->size++; | |
| 144 | + | |
| 145 | + added = 1; | |
| 146 | + } | |
| 147 | + | |
| 148 | + return added; | |
| 149 | +} | |
| 150 | + | |
| 151 | +// Remove noteVal. return 1 if removed, 0 if not | |
| 152 | +int tStack_remove(tStack* const stack, uint16_t noteVal) | |
| 153 | +{ | |
| 154 | + _tStack* ns = *stack; | |
| 155 | + | |
| 156 | + uint8_t k; | |
| 157 | + int foundIndex = tStack_contains(stack, noteVal); | |
| 158 | + int removed = 0; | |
| 159 | + | |
| 160 | + if (foundIndex >= 0) | |
| 161 | + { | |
| 162 | + for (k = 0; k < (ns->size - foundIndex); k++) | |
| 163 | + { | |
| 164 | + if ((k+foundIndex) >= (ns->capacity - 1)) | |
| 165 | + { | |
| 166 | + ns->data[k + foundIndex] = -1; | |
| 167 | + } | |
| 168 | + else | |
| 169 | + { | |
| 170 | + ns->data[k + foundIndex] = ns->data[k + foundIndex + 1]; | |
| 171 | + if ((k + foundIndex) == (ns->size - 1)) | |
| 172 | + { | |
| 173 | + ns->data[k + foundIndex + 1] = -1; | |
| 174 | + } | |
| 175 | + } | |
| 176 | + | |
| 177 | + } | |
| 178 | + // in case it got put on the stack multiple times | |
| 179 | + foundIndex--; | |
| 180 | + ns->size--; | |
| 181 | + removed = 1; | |
| 182 | + } | |
| 183 | + | |
| 184 | + return removed; | |
| 185 | +} | |
| 186 | + | |
| 187 | +// Doesn't change size of data types | |
| 188 | +void tStack_setCapacity(tStack* const stack, uint16_t cap) | |
| 189 | +{ | |
| 190 | + _tStack* ns = *stack; | |
| 191 | + | |
| 192 | + if (cap <= 0) | |
| 193 | + ns->capacity = 1; | |
| 194 | + else if (cap <= STACK_SIZE) | |
| 195 | + ns->capacity = cap; | |
| 196 | + else | |
| 197 | + ns->capacity = STACK_SIZE; | |
| 198 | + | |
| 199 | + for (int i = cap; i < STACK_SIZE; i++) | |
| 200 | + { | |
| 201 | + if ((int)ns->data[i] != -1) | |
| 202 | + { | |
| 203 | + ns->data[i] = -1; | |
| 204 | + ns->size -= 1; | |
| 205 | + } | |
| 206 | + } | |
| 207 | + | |
| 208 | + if (ns->pos >= cap) | |
| 209 | + { | |
| 210 | + ns->pos = 0; | |
| 211 | + } | |
| 212 | +} | |
| 213 | + | |
| 214 | +int tStack_getSize(tStack* const stack) | |
| 215 | +{ | |
| 216 | + _tStack* ns = *stack; | |
| 217 | + | |
| 218 | + return ns->size; | |
| 219 | +} | |
| 220 | + | |
| 221 | +void tStack_clear(tStack* const stack) | |
| 222 | +{ | |
| 223 | + _tStack* ns = *stack; | |
| 224 | + | |
| 225 | + for (int i = 0; i < STACK_SIZE; i++) | |
| 226 | + { | |
| 227 | + ns->data[i] = -1; | |
| 228 | + } | |
| 229 | + ns->pos = 0; | |
| 230 | + ns->size = 0; | |
| 231 | +} | |
| 232 | + | |
| 233 | +// Next item in order of addition to stack. Return 0-31 if there is a next item to move to. Returns -1 otherwise. | |
| 234 | +int tStack_next(tStack* const stack) | |
| 235 | +{ | |
| 236 | + _tStack* ns = *stack; | |
| 237 | + | |
| 238 | + int step = 0; | |
| 239 | + if (ns->size != 0) // if there is at least one note in the stack | |
| 240 | + { | |
| 241 | + if (ns->pos > 0) // if you're not at the most recent note (first one), then go backward in the array (moving from earliest to latest) | |
| 242 | + { | |
| 243 | + ns->pos--; | |
| 244 | + } | |
| 245 | + else | |
| 246 | + { | |
| 247 | + ns->pos = (ns->size - 1); // if you are the most recent note, go back to the earliest note in the array | |
| 248 | + } | |
| 249 | + | |
| 250 | + step = ns->data[ns->pos]; | |
| 251 | + return step; | |
| 252 | + } | |
| 253 | + else | |
| 254 | + { | |
| 255 | + return -1; | |
| 256 | + } | |
| 257 | +} | |
| 258 | + | |
| 259 | +int tStack_get(tStack* const stack, int which) | |
| 260 | +{ | |
| 261 | + _tStack* ns = *stack; | |
| 262 | + return ns->data[which]; | |
| 263 | +} | |
| 264 | + | |
| 265 | +int tStack_first(tStack* const stack) | |
| 266 | +{ | |
| 267 | + _tStack* ns = *stack; | |
| 268 | + return ns->data[0]; | |
| 269 | +} | |
| 270 | + | |
| 271 | + | |
| 272 | +// POLY | |
| 273 | +void tPoly_init(tPoly* const polyh, int maxNumVoices) | |
| 274 | +{ | |
| 275 | + _tPoly* poly = *polyh = (_tPoly*) leaf_alloc(sizeof(_tPoly)); | |
| 276 | + | |
| 277 | + poly->numVoices = maxNumVoices; | |
| 278 | + poly->maxNumVoices = maxNumVoices; | |
| 279 | + poly->lastVoiceToChange = 0; | |
| 280 | + | |
| 281 | + // Arp mode stuff | |
| 282 | + poly->currentVoice = 0; | |
| 283 | + poly->maxLength = 128; | |
| 284 | + poly->currentNote = -1; | |
| 285 | + | |
| 286 | + //default learned CCs and notes are just the CCs 1-128 - notes are skipped | |
| 287 | + for (int i = 0; i < 128; i++) | |
| 288 | + { | |
| 289 | + poly->notes[i][0] = 0; | |
| 290 | + poly->notes[i][1] = -1; | |
| 291 | + } | |
| 292 | + | |
| 293 | + poly->glideTime = 5.0f; | |
| 294 | + | |
| 295 | + poly->ramps = (tRamp*) leaf_alloc(sizeof(tRamp) * poly->maxNumVoices); | |
| 296 | + poly->rampVals = (float*) leaf_alloc(sizeof(float) * poly->maxNumVoices); | |
| 297 | + poly->firstReceived = (oBool*) leaf_alloc(sizeof(oBool) * poly->maxNumVoices); | |
| 298 | + poly->voices = (int**) leaf_alloc(sizeof(int*) * poly->maxNumVoices); | |
| 299 | + | |
| 300 | + for (int i = 0; i < poly->maxNumVoices; ++i) | |
| 301 | + { | |
| 302 | + poly->voices[i] = (int*) leaf_alloc(sizeof(int) * 2); | |
| 303 | + poly->voices[i][0] = -1; | |
| 304 | + poly->firstReceived[i] = OFALSE; | |
| 305 | + | |
| 306 | + tRamp_init(&poly->ramps[i], poly->glideTime, 1); | |
| 307 | + } | |
| 308 | + | |
| 309 | + poly->pitchBend = 0.0f; | |
| 310 | + | |
| 311 | + tRamp_init(&poly->pitchBendRamp, 1.0f, 1); | |
| 312 | + tStack_init(&poly->stack); | |
| 313 | + tStack_init(&poly->orderStack); | |
| 314 | + | |
| 315 | + poly->pitchGlideIsActive = OFALSE; | |
| 316 | +} | |
| 317 | + | |
| 318 | +void tPoly_free(tPoly* const polyh) | |
| 319 | +{ | |
| 320 | + _tPoly* poly = *polyh; | |
| 321 | + | |
| 322 | + for (int i = 0; i < poly->maxNumVoices; i++) | |
| 323 | + { | |
| 324 | + tRamp_free(&poly->ramps[i]); | |
| 325 | + leaf_free((char*)poly->voices[i]); | |
| 326 | + } | |
| 327 | + tRamp_free(&poly->pitchBendRamp); | |
| 328 | + tStack_free(&poly->stack); | |
| 329 | + tStack_free(&poly->orderStack); | |
| 330 | + | |
| 331 | + leaf_free((char*)poly->voices); | |
| 332 | + leaf_free((char*)poly->ramps); | |
| 333 | + leaf_free((char*)poly->rampVals); | |
| 334 | + leaf_free((char*)poly->firstReceived); | |
| 335 | + | |
| 336 | + leaf_free((char*)poly); | |
| 337 | +} | |
| 338 | + | |
| 339 | +void tPoly_initToPool (tPoly* const polyh, int maxNumVoices, tMempool* const mp) | |
| 340 | +{ | |
| 341 | + _tMempool* m = *mp; | |
| 342 | + _tPoly* poly = *polyh = (_tPoly*) mpool_alloc(sizeof(_tPoly), m); | |
| 343 | + | |
| 344 | + poly->numVoices = maxNumVoices; | |
| 345 | + poly->maxNumVoices = maxNumVoices; | |
| 346 | + poly->lastVoiceToChange = 0; | |
| 347 | + | |
| 348 | + // Arp mode stuff | |
| 349 | + poly->currentVoice = 0; | |
| 350 | + poly->maxLength = 128; | |
| 351 | + poly->currentNote = -1; | |
| 352 | + | |
| 353 | + //default learned CCs and notes are just the CCs 1-128 - notes are skipped | |
| 354 | + for (int i = 0; i < 128; i++) | |
| 355 | + { | |
| 356 | + poly->notes[i][0] = 0; | |
| 357 | + poly->notes[i][1] = -1; | |
| 358 | + } | |
| 359 | + | |
| 360 | + poly->glideTime = 5.0f; | |
| 361 | + | |
| 362 | + poly->ramps = (tRamp*) mpool_alloc(sizeof(tRamp) * poly->maxNumVoices, m); | |
| 363 | + poly->rampVals = (float*) mpool_alloc(sizeof(float) * poly->maxNumVoices, m); | |
| 364 | + poly->firstReceived = (oBool*) mpool_alloc(sizeof(oBool) * poly->maxNumVoices, m); | |
| 365 | + poly->voices = (int**) mpool_alloc(sizeof(int*) * poly->maxNumVoices, m); | |
| 366 | + | |
| 367 | + for (int i = 0; i < poly->maxNumVoices; ++i) | |
| 368 | + { | |
| 369 | + poly->voices[i] = (int*) mpool_alloc(sizeof(int) * 2, m); | |
| 370 | + poly->voices[i][0] = -1; | |
| 371 | + poly->firstReceived[i] = OFALSE; | |
| 372 | + | |
| 373 | + tRamp_initToPool(&poly->ramps[i], poly->glideTime, 1, mp); | |
| 374 | + } | |
| 375 | + | |
| 376 | + poly->pitchBend = 0.0f; | |
| 377 | + | |
| 378 | + tRamp_initToPool(&poly->pitchBendRamp, 1.0f, 1, mp); | |
| 379 | + tStack_initToPool(&poly->stack, mp); | |
| 380 | + tStack_initToPool(&poly->orderStack, mp); | |
| 381 | + | |
| 382 | + poly->pitchGlideIsActive = OFALSE; | |
| 383 | +} | |
| 384 | + | |
| 385 | +void tPoly_freeFromPool (tPoly* const polyh, tMempool* const mp) | |
| 386 | +{ | |
| 387 | + _tMempool* m = *mp; | |
| 388 | + _tPoly* poly = *polyh; | |
| 389 | + | |
| 390 | + for (int i = 0; i < poly->maxNumVoices; i++) | |
| 391 | + { | |
| 392 | + tRamp_freeFromPool(&poly->ramps[i], mp); | |
| 393 | + mpool_free((char*)poly->voices[i], m); | |
| 394 | + } | |
| 395 | + tRamp_freeFromPool(&poly->pitchBendRamp, mp); | |
| 396 | + tStack_freeFromPool(&poly->stack, mp); | |
| 397 | + tStack_freeFromPool(&poly->orderStack, mp); | |
| 398 | + | |
| 399 | + mpool_free((char*)poly->voices, m); | |
| 400 | + mpool_free((char*)poly->ramps, m); | |
| 401 | + mpool_free((char*)poly->rampVals, m); | |
| 402 | + mpool_free((char*)poly->firstReceived, m); | |
| 403 | + | |
| 404 | + mpool_free((char*)poly, m); | |
| 405 | +} | |
| 406 | + | |
| 407 | +void tPoly_tickPitch(tPoly* polyh) | |
| 408 | +{ | |
| 409 | + tPoly_tickPitchGlide(polyh); | |
| 410 | + tPoly_tickPitchBend(polyh); | |
| 411 | +} | |
| 412 | + | |
| 413 | +void tPoly_tickPitchGlide(tPoly* polyh) | |
| 414 | +{ | |
| 415 | + _tPoly* poly = *polyh; | |
| 416 | + for (int i = 0; i < poly->maxNumVoices; ++i) | |
| 417 | + { | |
| 418 | + tRamp_tick(&poly->ramps[i]); | |
| 419 | + } | |
| 420 | +} | |
| 421 | + | |
| 422 | +void tPoly_tickPitchBend(tPoly* polyh) | |
| 423 | +{ | |
| 424 | + _tPoly* poly = *polyh; | |
| 425 | + tRamp_tick(&poly->pitchBendRamp); | |
| 426 | +} | |
| 427 | + | |
| 428 | +void tPoly_setPitchBend(tPoly* const polyh, float pitchBend) | |
| 429 | +{ | |
| 430 | + _tPoly* poly = *polyh; | |
| 431 | + poly->pitchBend = pitchBend; | |
| 432 | + tRamp_setDest(&poly->pitchBendRamp, poly->pitchBend); | |
| 433 | +} | |
| 434 | + | |
| 435 | +int tPoly_noteOn(tPoly* const polyh, int note, uint8_t vel) | |
| 436 | +{ | |
| 437 | + _tPoly* poly = *polyh; | |
| 438 | + | |
| 439 | + // if not in keymap or already on stack, dont do anything. else, add that note. | |
| 440 | + if (tStack_contains(&poly->stack, note) >= 0) return -1; | |
| 441 | + else | |
| 442 | + { | |
| 443 | + tPoly_orderedAddToStack(polyh, note); | |
| 444 | + tStack_add(&poly->stack, note); | |
| 445 | + | |
| 446 | + int alteredVoice = -1; | |
| 447 | + oBool found = OFALSE; | |
| 448 | + for (int i = 0; i < poly->numVoices; i++) | |
| 449 | + { | |
| 450 | + if (poly->voices[i][0] < 0) // if inactive voice, give this note to voice | |
| 451 | + { | |
| 452 | + if (!poly->firstReceived[i] || !poly->pitchGlideIsActive) | |
| 453 | + { | |
| 454 | + tRamp_setVal(&poly->ramps[i], note); | |
| 455 | + poly->firstReceived[i] = OTRUE; | |
| 456 | + } | |
| 457 | + | |
| 458 | + found = OTRUE; | |
| 459 | + | |
| 460 | + poly->voices[i][0] = note; | |
| 461 | + poly->voices[i][1] = vel; | |
| 462 | + poly->lastVoiceToChange = i; | |
| 463 | + poly->notes[note][0] = vel; | |
| 464 | + poly->notes[note][1] = i; | |
| 465 | + | |
| 466 | + tRamp_setDest(&poly->ramps[i], poly->voices[i][0]); | |
| 467 | + | |
| 468 | + alteredVoice = i; | |
| 469 | + break; | |
| 470 | + } | |
| 471 | + } | |
| 472 | + | |
| 473 | + if (!found) //steal | |
| 474 | + { | |
| 475 | + int whichVoice, whichNote; | |
| 476 | + for (int j = tStack_getSize(&poly->stack) - 1; j >= 0; j--) | |
| 477 | + { | |
| 478 | + whichNote = tStack_get(&poly->stack, j); | |
| 479 | + whichVoice = poly->notes[whichNote][1]; | |
| 480 | + if (whichVoice >= 0) | |
| 481 | + { | |
| 482 | + poly->lastVoiceToChange = whichVoice; | |
| 483 | + int oldNote = poly->voices[whichVoice][0]; | |
| 484 | + poly->voices[whichVoice][0] = note; | |
| 485 | + poly->voices[whichVoice][1] = vel; | |
| 486 | + poly->notes[oldNote][1] = -1; //mark the stolen voice as inactive (in the second dimension of the notes array) | |
| 487 | + poly->notes[note][0] = vel; | |
| 488 | + poly->notes[note][1] = whichVoice; | |
| 489 | + | |
| 490 | + if (poly->pitchGlideIsActive) | |
| 491 | + { | |
| 492 | + tRamp_setTime(&poly->ramps[whichVoice], poly->glideTime); | |
| 493 | + } | |
| 494 | + else | |
| 495 | + { | |
| 496 | + tRamp_setVal(&poly->ramps[whichVoice], note); | |
| 497 | + } | |
| 498 | + tRamp_setDest(&poly->ramps[whichVoice], poly->voices[whichVoice][0]); | |
| 499 | + | |
| 500 | + alteredVoice = whichVoice; | |
| 501 | + | |
| 502 | + break; | |
| 503 | + } | |
| 504 | + } | |
| 505 | + } | |
| 506 | + return alteredVoice; | |
| 507 | + } | |
| 508 | +} | |
| 509 | + | |
| 510 | + | |
| 511 | +int16_t noteToTest = -1; | |
| 512 | + | |
| 513 | +int tPoly_noteOff(tPoly* const polyh, uint8_t note) | |
| 514 | +{ | |
| 515 | + _tPoly* poly = *polyh; | |
| 516 | + | |
| 517 | + tStack_remove(&poly->stack, note); | |
| 518 | + tStack_remove(&poly->orderStack, note); | |
| 519 | + poly->notes[note][0] = 0; | |
| 520 | + poly->notes[note][1] = -1; | |
| 521 | + | |
| 522 | + int deactivatedVoice = -1; | |
| 523 | + for (int i = 0; i < poly->maxNumVoices; i++) | |
| 524 | + { | |
| 525 | + if (poly->voices[i][0] == note) | |
| 526 | + { | |
| 527 | + poly->voices[i][0] = -1; | |
| 528 | + poly->voices[i][1] = 0; | |
| 529 | + poly->lastVoiceToChange = i; | |
| 530 | + deactivatedVoice = i; | |
| 531 | + break; | |
| 532 | + } | |
| 533 | + } | |
| 534 | + /* | |
| 535 | + //monophonic handling | |
| 536 | + if ((poly->numVoices == 1) && (tStack_getSize(poly->stack) > 0)) | |
| 537 | + { | |
| 538 | + int oldNote = tStack_first(poly->stack); | |
| 539 | + poly->voices[0][0] = oldNote; | |
| 540 | + poly->voices[0][1] = poly->notes[oldNote][0]; | |
| 541 | + poly->lastVoiceToChange = 0; | |
| 542 | + } | |
| 543 | + */ | |
| 544 | + | |
| 545 | + //grab old notes off the stack if there are notes waiting to replace the free voice | |
| 546 | + if (deactivatedVoice >= 0) | |
| 547 | + { | |
| 548 | + for (int j = 0; j < tStack_getSize(&poly->stack); ++j) | |
| 549 | + { | |
| 550 | + noteToTest = tStack_get(&poly->stack, j); | |
| 551 | + | |
| 552 | + if (poly->notes[noteToTest][1] < 0) //if there is a stolen note waiting (marked inactive but on the stack) | |
| 553 | + { | |
| 554 | + poly->voices[deactivatedVoice][0] = noteToTest; //set the newly free voice to use the old stolen note | |
| 555 | + if (poly->pitchGlideIsActive) | |
| 556 | + { | |
| 557 | + tRamp_setTime(&poly->ramps[deactivatedVoice], poly->glideTime); | |
| 558 | + } | |
| 559 | + else | |
| 560 | + { | |
| 561 | + tRamp_setVal(&poly->ramps[deactivatedVoice], noteToTest); | |
| 562 | + } | |
| 563 | + tRamp_setDest(&poly->ramps[deactivatedVoice], poly->voices[deactivatedVoice][0]); | |
| 564 | + poly->voices[deactivatedVoice][1] = poly->notes[noteToTest][0]; // set the velocity of the voice to be the velocity of that note | |
| 565 | + poly->notes[noteToTest][1] = deactivatedVoice; //mark that it is no longer stolen and is now active | |
| 566 | + return -1; | |
| 567 | + } | |
| 568 | + } | |
| 569 | + } | |
| 570 | + return deactivatedVoice; | |
| 571 | +} | |
| 572 | + | |
| 573 | +void tPoly_orderedAddToStack(tPoly* const polyh, uint8_t noteVal) | |
| 574 | +{ | |
| 575 | + _tPoly* poly = *polyh; | |
| 576 | + | |
| 577 | + uint8_t j; | |
| 578 | + int myPitch, thisPitch, nextPitch; | |
| 579 | + | |
| 580 | + tStack ns = poly->orderStack; | |
| 581 | + | |
| 582 | + int whereToInsert = 0; | |
| 583 | + | |
| 584 | + for (j = 0; j < ns->size; j++) | |
| 585 | + { | |
| 586 | + myPitch = noteVal; | |
| 587 | + thisPitch = ns->data[j]; | |
| 588 | + nextPitch = ns->data[j+1]; | |
| 589 | + | |
| 590 | + if (myPitch > thisPitch) | |
| 591 | + { | |
| 592 | + if ((myPitch < nextPitch) || (nextPitch == -1)) | |
| 593 | + { | |
| 594 | + whereToInsert = j+1; | |
| 595 | + break; | |
| 596 | + } | |
| 597 | + } | |
| 598 | + } | |
| 599 | + | |
| 600 | + //first move notes that are already in the stack one position to the right | |
| 601 | + for (j = ns->size; j > whereToInsert; j--) | |
| 602 | + { | |
| 603 | + ns->data[j] = ns->data[(j - 1)]; | |
| 604 | + } | |
| 605 | + | |
| 606 | + //then, insert the new note into the front of the stack | |
| 607 | + ns->data[whereToInsert] = noteVal; | |
| 608 | + | |
| 609 | + ns->size++; | |
| 610 | + | |
| 611 | +} | |
| 612 | + | |
| 613 | +void tPoly_setNumVoices(tPoly* const polyh, uint8_t numVoices) | |
| 614 | +{ | |
| 615 | + _tPoly* poly = *polyh; | |
| 616 | + poly->numVoices = (numVoices > poly->maxNumVoices) ? poly->maxNumVoices : numVoices; | |
| 617 | +} | |
| 618 | + | |
| 619 | +void tPoly_setPitchGlideActive(tPoly* const polyh, oBool isActive) | |
| 620 | +{ | |
| 621 | + _tPoly* poly = *polyh; | |
| 622 | + poly->pitchGlideIsActive = isActive; | |
| 623 | +} | |
| 624 | + | |
| 625 | +void tPoly_setPitchGlideTime(tPoly* const polyh, float t) | |
| 626 | +{ | |
| 627 | + _tPoly* poly = *polyh; | |
| 628 | + poly->glideTime = t; | |
| 629 | + for (int i = 0; i < poly->maxNumVoices; ++i) | |
| 630 | + { | |
| 631 | + tRamp_setTime(&poly->ramps[i], poly->glideTime); | |
| 632 | + } | |
| 633 | +} | |
| 634 | + | |
| 635 | +void tPoly_setBendGlideTime(tPoly* const polyh, float t) | |
| 636 | +{ | |
| 637 | + _tPoly* poly = *polyh; | |
| 638 | + tRamp_setTime(&poly->pitchBendRamp, t); | |
| 639 | +} | |
| 640 | + | |
| 641 | +void tPoly_setBendSamplesPerTick(tPoly* const polyh, float t) | |
| 642 | +{ | |
| 643 | + _tPoly* poly = *polyh; | |
| 644 | + poly->pitchBendRamp->samples_per_tick = t; | |
| 645 | +} | |
| 646 | + | |
| 647 | +int tPoly_getNumVoices(tPoly* const polyh) | |
| 648 | +{ | |
| 649 | + _tPoly* poly = *polyh; | |
| 650 | + return poly->numVoices; | |
| 651 | +} | |
| 652 | + | |
| 653 | +int tPoly_getNumActiveVoices(tPoly* const polyh) | |
| 654 | +{ | |
| 655 | + _tPoly* poly = *polyh; | |
| 656 | + return LEAF_clip(0, tStack_getSize(&poly->stack), poly->numVoices); | |
| 657 | +} | |
| 658 | + | |
| 659 | +float tPoly_getPitch(tPoly* const polyh, uint8_t voice) | |
| 660 | +{ | |
| 661 | + _tPoly* poly = *polyh; | |
| 662 | + return tRamp_sample(&poly->ramps[voice]) + tRamp_sample(&poly->pitchBendRamp); | |
| 663 | +} | |
| 664 | + | |
| 665 | +int tPoly_getKey(tPoly* const polyh, uint8_t voice) | |
| 666 | +{ | |
| 667 | + _tPoly* poly = *polyh; | |
| 668 | + return poly->voices[voice][0]; | |
| 669 | +} | |
| 670 | + | |
| 671 | +int tPoly_getVelocity(tPoly* const polyh, uint8_t voice) | |
| 672 | +{ | |
| 673 | + _tPoly* poly = *polyh; | |
| 674 | + return poly->voices[voice][1]; | |
| 675 | +} | |
| 676 | + | |
| 677 | +int tPoly_isOn(tPoly* const polyh, uint8_t voice) | |
| 678 | +{ | |
| 679 | + _tPoly* poly = *polyh; | |
| 680 | + return (poly->voices[voice][0] > 0) ? 1 : 0; | |
| 681 | +} | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | +//tSimplePoly = much more efficient implementation without ramps and glide | |
| 687 | + | |
| 688 | + | |
| 689 | +// SIMPLE POLY | |
| 690 | +void tSimplePoly_init(tSimplePoly* const polyh, int maxNumVoices) | |
| 691 | +{ | |
| 692 | + tSimplePoly_initToPool(polyh, maxNumVoices, &leaf.mempool); | |
| 693 | +} | |
| 694 | + | |
| 695 | +void tSimplePoly_free(tSimplePoly* const polyh) | |
| 696 | +{ | |
| 697 | + tSimplePoly_freeFromPool(polyh, &leaf.mempool); | |
| 698 | +} | |
| 699 | + | |
| 700 | +void tSimplePoly_initToPool (tSimplePoly* const polyh, int maxNumVoices, tMempool* const mp) | |
| 701 | +{ | |
| 702 | + _tMempool* m = *mp; | |
| 703 | + _tSimplePoly* poly = *polyh = (_tSimplePoly*) mpool_alloc(sizeof(_tSimplePoly), m); | |
| 704 | + | |
| 705 | + poly->numVoices = maxNumVoices; | |
| 706 | + poly->maxNumVoices = maxNumVoices; | |
| 707 | + | |
| 708 | + for (int i = 0; i < 128; i++) | |
| 709 | + { | |
| 710 | + poly->notes[i][0] = -1; | |
| 711 | + poly->notes[i][1] = 0; | |
| 712 | + } | |
| 713 | + poly->stealing_on = 1; | |
| 714 | + poly->recover_stolen = 1; | |
| 715 | + poly->voices = (int**) mpool_alloc(sizeof(int*) * poly->maxNumVoices, m); | |
| 716 | + | |
| 717 | + for (int i = 0; i < poly->maxNumVoices; ++i) | |
| 718 | + { | |
| 719 | + poly->voices[i] = (int*) mpool_alloc(sizeof(int) * 3, m); | |
| 720 | + poly->voices[i][0] = -1; | |
| 721 | + } | |
| 722 | + tStack_initToPool(&poly->stack, mp); | |
| 723 | + | |
| 724 | +} | |
| 725 | + | |
| 726 | +void tSimplePoly_freeFromPool (tSimplePoly* const polyh, tMempool* const mp) | |
| 727 | +{ | |
| 728 | + _tMempool* m = *mp; | |
| 729 | + _tSimplePoly* poly = *polyh; | |
| 730 | + | |
| 731 | + for (int i = 0; i < poly->maxNumVoices; i++) | |
| 732 | + { | |
| 733 | + mpool_free((char*)poly->voices[i], m); | |
| 734 | + } | |
| 735 | + tStack_freeFromPool(&poly->stack, mp); | |
| 736 | + mpool_free((char*)poly->voices, m); | |
| 737 | + mpool_free((char*)poly, m); | |
| 738 | +} | |
| 739 | + | |
| 740 | +int tSimplePoly_noteOn(tSimplePoly* const polyh, int note, uint8_t vel) | |
| 741 | +{ | |
| 742 | + _tSimplePoly* poly = *polyh; | |
| 743 | + int whichVoice, whichNote, oldNote, alteredVoice; | |
| 744 | + // if not in keymap or already on stack, dont do anything. else, add that note. | |
| 745 | + if (tStack_contains(&poly->stack, note) >= 0) return -1; | |
| 746 | + else | |
| 747 | + { | |
| 748 | + alteredVoice = -1; | |
| 749 | + oBool found = OFALSE; | |
| 750 | + for (int i = 0; i < poly->numVoices; i++) | |
| 751 | + { | |
| 752 | + if (poly->voices[i][0] == -1) // if inactive voice, give this note to voice | |
| 753 | + { | |
| 754 | + | |
| 755 | + found = OTRUE; | |
| 756 | + | |
| 757 | + poly->voices[i][0] = note; | |
| 758 | + poly->voices[i][1] = vel; | |
| 759 | + poly->notes[note][0] = i; | |
| 760 | + poly->notes[note][1] = vel; | |
| 761 | + poly->voices[i][2] = note; // voices[i][2] is the output midi note, (avoiding the -1 when a voice is inactive) | |
| 762 | + | |
| 763 | + alteredVoice = i; | |
| 764 | + tStack_add(&poly->stack, note); | |
| 765 | + break; | |
| 766 | + } | |
| 767 | + } | |
| 768 | + if (!found) | |
| 769 | + { | |
| 770 | + //second preference is grabbing one that is in release phase but not finished sounding yet | |
| 771 | + for (int i = 0 ; i < poly->numVoices; i++) | |
| 772 | + { | |
| 773 | + if (poly->voices[i][0] == -2) // if voice is released but still sounding, take over this voice | |
| 774 | + { | |
| 775 | + | |
| 776 | + found = OTRUE; | |
| 777 | + | |
| 778 | + poly->voices[i][0] = note; | |
| 779 | + poly->voices[i][1] = vel; | |
| 780 | + poly->notes[note][0] = i; | |
| 781 | + poly->notes[note][1] = vel; | |
| 782 | + poly->voices[i][2] = note; // voices[i][2] is the output midi note, (avoiding the -1 when a voice is inactive) | |
| 783 | + | |
| 784 | + alteredVoice = i; | |
| 785 | + tStack_add(&poly->stack, note); | |
| 786 | + break; | |
| 787 | + } | |
| 788 | + } | |
| 789 | + } | |
| 790 | + if ((!found) && (poly->stealing_on)) //steal | |
| 791 | + { | |
| 792 | + for (int j = tStack_getSize(&poly->stack) - 1; j >= 0; j--) | |
| 793 | + { | |
| 794 | + whichNote = tStack_get(&poly->stack, j); | |
| 795 | + whichVoice = poly->notes[whichNote][0]; | |
| 796 | + if (whichVoice >= 0) | |
| 797 | + { | |
| 798 | + oldNote = poly->voices[whichVoice][0]; | |
| 799 | + poly->voices[whichVoice][0] = note; | |
| 800 | + poly->voices[whichVoice][1] = vel; | |
| 801 | + poly->notes[oldNote][0] = -3; //mark the stolen voice as stolen (in the second dimension of the notes array) | |
| 802 | + poly->notes[note][0] = whichVoice; | |
| 803 | + poly->notes[note][1] = vel; | |
| 804 | + | |
| 805 | + poly->voices[whichVoice][2] = note; | |
| 806 | + | |
| 807 | + alteredVoice = whichVoice; | |
| 808 | + tStack_add(&poly->stack, note); | |
| 809 | + break; | |
| 810 | + } | |
| 811 | + } | |
| 812 | + } | |
| 813 | + return alteredVoice; | |
| 814 | + } | |
| 815 | +} | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | +int tSimplePoly_noteOff(tSimplePoly* const polyh, uint8_t note) | |
| 820 | +{ | |
| 821 | + _tSimplePoly* poly = *polyh; | |
| 822 | + int16_t noteToTest = -1; | |
| 823 | + | |
| 824 | + tStack_remove(&poly->stack, note); | |
| 825 | + poly->notes[note][0] = -1; | |
| 826 | + | |
| 827 | + int deactivatedVoice = -1; | |
| 828 | + for (int i = 0; i < poly->maxNumVoices; i++) | |
| 829 | + { | |
| 830 | + if (poly->voices[i][0] == note) | |
| 831 | + { | |
| 832 | + poly->voices[i][0] = -1; | |
| 833 | + poly->voices[i][1] = 0; | |
| 834 | + deactivatedVoice = i; | |
| 835 | + break; | |
| 836 | + } | |
| 837 | + } | |
| 838 | + | |
| 839 | + if (poly->recover_stolen) | |
| 840 | + { | |
| 841 | + //grab old notes off the stack if there are notes waiting to replace the free voice | |
| 842 | + if (deactivatedVoice >= 0) | |
| 843 | + { | |
| 844 | + for (int j = 0; j < tStack_getSize(&poly->stack); ++j) | |
| 845 | + { | |
| 846 | + noteToTest = tStack_get(&poly->stack, j); | |
| 847 | + | |
| 848 | + if (poly->notes[noteToTest][0] == -3) //if there is a stolen note waiting (marked inactive but on the stack) | |
| 849 | + { | |
| 850 | + poly->voices[deactivatedVoice][0] = noteToTest; //set the newly free voice to use the old stolen note | |
| 851 | + poly->voices[deactivatedVoice][1] = poly->notes[noteToTest][1]; // set the velocity of the voice to be the velocity of that note | |
| 852 | + poly->voices[deactivatedVoice][2] = noteToTest; | |
| 853 | + poly->notes[noteToTest][0] = deactivatedVoice; //mark that it is no longer stolen and is now active | |
| 854 | + return -1; | |
| 855 | + } | |
| 856 | + } | |
| 857 | + } | |
| 858 | + } | |
| 859 | + return deactivatedVoice; | |
| 860 | +} | |
| 861 | + | |
| 862 | + | |
| 863 | +void tSimplePoly_deactivateVoice(tSimplePoly* const polyh, uint8_t voice) | |
| 864 | +{ | |
| 865 | + _tSimplePoly* poly = *polyh; | |
| 866 | + | |
| 867 | + if (poly->voices[voice][0] == -2) //only do this if the voice is waiting for deactivation (not already reassigned while waiting) | |
| 868 | + { | |
| 869 | + poly->voices[voice][0] = -1; | |
| 870 | + poly->voices[voice][1] = 0; | |
| 871 | + if (poly->recover_stolen) | |
| 872 | + { | |
| 873 | + //grab old notes off the stack if there are notes waiting to replace the free voice | |
| 874 | + for (int j = 0; j < tStack_getSize(&poly->stack); ++j) | |
| 875 | + { | |
| 876 | + noteToTest = tStack_get(&poly->stack, j); | |
| 877 | + | |
| 878 | + if (poly->notes[noteToTest][0] == -3) //if there is a stolen note waiting (marked inactive but on the stack) | |
| 879 | + { | |
| 880 | + poly->voices[voice][0] = noteToTest; //set the newly free voice to use the old stolen note | |
| 881 | + poly->voices[voice][1] = poly->notes[noteToTest][1]; // set the velocity of the voice to be the velocity of that note | |
| 882 | + poly->voices[voice][2] = noteToTest; | |
| 883 | + poly->notes[noteToTest][0] = voice; //mark that it is no longer stolen and is now active | |
| 884 | + } | |
| 885 | + } | |
| 886 | + } | |
| 887 | + } | |
| 888 | +} | |
| 889 | + | |
| 890 | +int tSimplePoly_findVoiceAssignedToNote(tSimplePoly* const polyh, uint8_t note) | |
| 891 | +{ | |
| 892 | + _tSimplePoly* poly = *polyh; | |
| 893 | + | |
| 894 | + | |
| 895 | + int voiceWithThatNote = -1; | |
| 896 | + for (int i = 0; i < poly->maxNumVoices; i++) | |
| 897 | + { | |
| 898 | + if (poly->voices[i][0] == note) | |
| 899 | + { | |
| 900 | + voiceWithThatNote = i; | |
| 901 | + break; | |
| 902 | + } | |
| 903 | + } | |
| 904 | + return voiceWithThatNote; | |
| 905 | +} | |
| 906 | + | |
| 907 | + | |
| 908 | +int tSimplePoly_markPendingNoteOff(tSimplePoly* const polyh, uint8_t note) | |
| 909 | +{ | |
| 910 | + _tSimplePoly* poly = *polyh; | |
| 911 | + int deactivatedVoice = -1; | |
| 912 | + | |
| 913 | + if (tStack_remove(&poly->stack, note)) | |
| 914 | + | |
| 915 | + { | |
| 916 | + | |
| 917 | + poly->notes[note][0] = -2; | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
| 921 | + for (int i = 0; i < poly->maxNumVoices; i++) | |
| 922 | + { | |
| 923 | + if (poly->voices[i][0] == note) | |
| 924 | + { | |
| 925 | + poly->voices[i][0] = -2; | |
| 926 | + poly->voices[i][1] = 0; | |
| 927 | + deactivatedVoice = i; | |
| 928 | + break; | |
| 929 | + } | |
| 930 | + } | |
| 931 | + | |
| 932 | + | |
| 933 | + } | |
| 934 | + return deactivatedVoice; | |
| 935 | +} | |
| 936 | + | |
| 937 | +void tSimplePoly_setNumVoices(tSimplePoly* const polyh, uint8_t numVoices) | |
| 938 | +{ | |
| 939 | + _tSimplePoly* poly = *polyh; | |
| 940 | + poly->numVoices = (numVoices > poly->maxNumVoices) ? poly->maxNumVoices : numVoices; | |
| 941 | +} | |
| 942 | + | |
| 943 | + | |
| 944 | +int tSimplePoly_getNumVoices(tSimplePoly* const polyh) | |
| 945 | +{ | |
| 946 | + _tSimplePoly* poly = *polyh; | |
| 947 | + return poly->numVoices; | |
| 948 | +} | |
| 949 | + | |
| 950 | +int tSimplePoly_getNumActiveVoices(tSimplePoly* const polyh) | |
| 951 | +{ | |
| 952 | + _tSimplePoly* poly = *polyh; | |
| 953 | + return LEAF_clip(0, tStack_getSize(&poly->stack), poly->numVoices); | |
| 954 | +} | |
| 955 | + | |
| 956 | + | |
| 957 | +int tSimplePoly_getPitch(tSimplePoly* const polyh, uint8_t voice) | |
| 958 | +{ | |
| 959 | + _tSimplePoly* poly = *polyh; | |
| 960 | + return poly->voices[voice][2]; | |
| 961 | +} | |
| 962 | + | |
| 963 | +//this one returns negative one if the voice is inactive | |
| 964 | +int tSimplePoly_getPitchAndCheckActive(tSimplePoly* const polyh, uint8_t voice) | |
| 965 | +{ | |
| 966 | + _tSimplePoly* poly = *polyh; | |
| 967 | + return poly->voices[voice][0]; | |
| 968 | +} | |
| 969 | + | |
| 970 | +int tSimplePoly_getVelocity(tSimplePoly* const polyh, uint8_t voice) | |
| 971 | +{ | |
| 972 | + _tSimplePoly* poly = *polyh; | |
| 973 | + return poly->voices[voice][1]; | |
| 974 | +} | |
| 975 | + | |
| 976 | +int tSimplePoly_isOn(tSimplePoly* const polyh, uint8_t voice) | |
| 977 | +{ | |
| 978 | + _tSimplePoly* poly = *polyh; | |
| 979 | + return (poly->voices[voice][0] > 0) ? 1 : 0; | |
| 980 | +} | |
| @@ -0,0 +1,1660 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + leaf-oscillators.c | |
| 3 | + Created: 20 Jan 2017 12:00:58pm | |
| 4 | + Author: Michael R Mulshine | |
| 5 | + ==============================================================================*/ | |
| 6 | + | |
| 7 | +#if _WIN32 || _WIN64 | |
| 8 | + | |
| 9 | +#include "..\Inc\leaf-oscillators.h" | |
| 10 | +#include "..\leaf.h" | |
| 11 | + | |
| 12 | +#else | |
| 13 | + | |
| 14 | +#include "../Inc/leaf-oscillators.h" | |
| 15 | +#include "../leaf.h" | |
| 16 | + | |
| 17 | +#endif | |
| 18 | + | |
| 19 | +// Cycle | |
| 20 | +void tCycle_init(tCycle* const cy) | |
| 21 | +{ | |
| 22 | + tCycle_initToPool(cy, &leaf.mempool); | |
| 23 | +} | |
| 24 | + | |
| 25 | +void tCycle_free(tCycle* const cy) | |
| 26 | +{ | |
| 27 | + tCycle_freeFromPool(cy, &leaf.mempool); | |
| 28 | +} | |
| 29 | + | |
| 30 | +void tCycle_initToPool (tCycle* const cy, tMempool* const mp) | |
| 31 | +{ | |
| 32 | + _tMempool* m = *mp; | |
| 33 | + _tCycle* c = *cy = (_tCycle*) mpool_alloc(sizeof(_tCycle), m); | |
| 34 | + | |
| 35 | + c->inc = 0.0f; | |
| 36 | + c->phase = 0.0f; | |
| 37 | + | |
| 38 | +} | |
| 39 | + | |
| 40 | +void tCycle_freeFromPool (tCycle* const cy, tMempool* const mp) | |
| 41 | +{ | |
| 42 | + _tMempool* m = *mp; | |
| 43 | + _tCycle* c = *cy; | |
| 44 | + | |
| 45 | + mpool_free((char*)c, m); | |
| 46 | +} | |
| 47 | + | |
| 48 | +void tCycle_setFreq(tCycle* const cy, float freq) | |
| 49 | +{ | |
| 50 | + _tCycle* c = *cy; | |
| 51 | + | |
| 52 | + c->freq = freq; | |
| 53 | + | |
| 54 | + c->inc = freq * leaf.invSampleRate; | |
| 55 | +} | |
| 56 | + | |
| 57 | +//need to check bounds and wrap table properly to allow through-zero FM | |
| 58 | +float tCycle_tick(tCycle* const cy) | |
| 59 | +{ | |
| 60 | + _tCycle* c = *cy; | |
| 61 | + float temp;; | |
| 62 | + int intPart;; | |
| 63 | + float fracPart; | |
| 64 | + float samp0; | |
| 65 | + float samp1; | |
| 66 | + | |
| 67 | + // Phasor increment | |
| 68 | + c->phase += c->inc; | |
| 69 | + while (c->phase >= 1.0f) c->phase -= 1.0f; | |
| 70 | + while (c->phase < 0.0f) c->phase += 1.0f; | |
| 71 | + | |
| 72 | + // Wavetable synthesis | |
| 73 | + | |
| 74 | + temp = SINE_TABLE_SIZE * c->phase; | |
| 75 | + intPart = (int)temp; | |
| 76 | + fracPart = temp - (float)intPart; | |
| 77 | + samp0 = __leaf_table_sinewave[intPart]; | |
| 78 | + if (++intPart >= SINE_TABLE_SIZE) intPart = 0; | |
| 79 | + samp1 = __leaf_table_sinewave[intPart]; | |
| 80 | + | |
| 81 | + return (samp0 + (samp1 - samp0) * fracPart); | |
| 82 | + | |
| 83 | +} | |
| 84 | + | |
| 85 | +void tCycleSampleRateChanged (tCycle* const cy) | |
| 86 | +{ | |
| 87 | + _tCycle* c = *cy; | |
| 88 | + | |
| 89 | + c->inc = c->freq * leaf.invSampleRate; | |
| 90 | +} | |
| 91 | + | |
| 92 | +//======================================================================== | |
| 93 | +/* Triangle */ | |
| 94 | +void tTriangle_init(tTriangle* const cy) | |
| 95 | +{ | |
| 96 | + tTriangle_initToPool(cy, &leaf.mempool); | |
| 97 | +} | |
| 98 | + | |
| 99 | +void tTriangle_free(tTriangle* const cy) | |
| 100 | +{ | |
| 101 | + tTriangle_freeFromPool(cy, &leaf.mempool); | |
| 102 | +} | |
| 103 | + | |
| 104 | +void tTriangle_initToPool (tTriangle* const cy, tMempool* const mp) | |
| 105 | +{ | |
| 106 | + _tMempool* m = *mp; | |
| 107 | + _tTriangle* c = *cy = (_tTriangle*) mpool_alloc(sizeof(_tTriangle), m); | |
| 108 | + | |
| 109 | + c->inc = 0.0f; | |
| 110 | + c->phase = 0.0f; | |
| 111 | + tTriangle_setFreq(cy, 220); | |
| 112 | +} | |
| 113 | + | |
| 114 | +void tTriangle_freeFromPool (tTriangle* const cy, tMempool* const mp) | |
| 115 | +{ | |
| 116 | + _tMempool* m = *mp; | |
| 117 | + _tTriangle* c = *cy; | |
| 118 | + | |
| 119 | + mpool_free((char*)c, m); | |
| 120 | +} | |
| 121 | + | |
| 122 | +void tTriangle_setFreq(tTriangle* const cy, float freq) | |
| 123 | +{ | |
| 124 | + _tTriangle* c = *cy; | |
| 125 | + | |
| 126 | + c->freq = freq; | |
| 127 | + | |
| 128 | + c->inc = c->freq * leaf.invSampleRate; | |
| 129 | + | |
| 130 | + c->w = c->freq * INV_20; | |
| 131 | + for (c->oct = 0; c->w > 2.0f; c->oct++) | |
| 132 | + { | |
| 133 | + c->w = 0.5f * c->w; | |
| 134 | + } | |
| 135 | + c->w = 2.0f - c->w; | |
| 136 | +} | |
| 137 | + | |
| 138 | + | |
| 139 | +float tTriangle_tick(tTriangle* const cy) | |
| 140 | +{ | |
| 141 | + _tTriangle* c = *cy; | |
| 142 | + | |
| 143 | + // Phasor increment | |
| 144 | + c->phase += c->inc; | |
| 145 | + while (c->phase >= 1.0f) c->phase -= 1.0f; | |
| 146 | + while (c->phase < 0.0f) c->phase += 1.0f; | |
| 147 | + | |
| 148 | + float out = 0.0f; | |
| 149 | + | |
| 150 | + int idx = (int)(c->phase * TRI_TABLE_SIZE); | |
| 151 | + | |
| 152 | + // Wavetable synthesis | |
| 153 | + out = __leaf_table_triangle[c->oct+1][idx] + | |
| 154 | + (__leaf_table_triangle[c->oct][idx] - __leaf_table_triangle[c->oct+1][idx]) * c->w; | |
| 155 | + | |
| 156 | + return out; | |
| 157 | +} | |
| 158 | + | |
| 159 | +void tTriangleSampleRateChanged (tTriangle* const cy) | |
| 160 | +{ | |
| 161 | + _tTriangle* c = *cy; | |
| 162 | + | |
| 163 | + c->inc = c->freq * leaf.invSampleRate; | |
| 164 | +} | |
| 165 | + | |
| 166 | +//======================================================================== | |
| 167 | +/* Square */ | |
| 168 | +void tSquare_init(tSquare* const cy) | |
| 169 | +{ | |
| 170 | + tSquare_initToPool(cy, &leaf.mempool); | |
| 171 | +} | |
| 172 | + | |
| 173 | +void tSquare_free(tSquare* const cy) | |
| 174 | +{ | |
| 175 | + tSquare_freeFromPool(cy, &leaf.mempool); | |
| 176 | +} | |
| 177 | + | |
| 178 | +void tSquare_initToPool (tSquare* const cy, tMempool* const mp) | |
| 179 | +{ | |
| 180 | + _tMempool* m = *mp; | |
| 181 | + _tSquare* c = *cy = (_tSquare*) mpool_alloc(sizeof(_tSquare), m); | |
| 182 | + | |
| 183 | + c->inc = 0.0f; | |
| 184 | + c->phase = 0.0f; | |
| 185 | + tSquare_setFreq(cy, 220); | |
| 186 | +} | |
| 187 | + | |
| 188 | +void tSquare_freeFromPool(tSquare* const cy, tMempool* const mp) | |
| 189 | +{ | |
| 190 | + _tMempool* m = *mp; | |
| 191 | + _tSquare* c = *cy; | |
| 192 | + | |
| 193 | + mpool_free((char*)c, m); | |
| 194 | +} | |
| 195 | + | |
| 196 | +void tSquare_setFreq(tSquare* const cy, float freq) | |
| 197 | +{ | |
| 198 | + _tSquare* c = *cy; | |
| 199 | + | |
| 200 | + c->freq = freq; | |
| 201 | + | |
| 202 | + c->inc = c->freq * leaf.invSampleRate; | |
| 203 | + | |
| 204 | + c->w = c->freq * INV_20; | |
| 205 | + for (c->oct = 0; c->w > 2.0f; c->oct++) | |
| 206 | + { | |
| 207 | + c->w = 0.5f * c->w; | |
| 208 | + } | |
| 209 | + c->w = 2.0f - c->w; | |
| 210 | +} | |
| 211 | + | |
| 212 | +float tSquare_tick(tSquare* const cy) | |
| 213 | +{ | |
| 214 | + _tSquare* c = *cy; | |
| 215 | + | |
| 216 | + // Phasor increment | |
| 217 | + c->phase += c->inc; | |
| 218 | + while (c->phase >= 1.0f) c->phase -= 1.0f; | |
| 219 | + while (c->phase < 0.0f) c->phase += 1.0f; | |
| 220 | + | |
| 221 | + float out = 0.0f; | |
| 222 | + | |
| 223 | + int idx = (int)(c->phase * SQR_TABLE_SIZE); | |
| 224 | + | |
| 225 | + // Wavetable synthesis | |
| 226 | + out = __leaf_table_squarewave[c->oct+1][idx] + | |
| 227 | + (__leaf_table_squarewave[c->oct][idx] - __leaf_table_squarewave[c->oct+1][idx]) * c->w; | |
| 228 | + | |
| 229 | + return out; | |
| 230 | +} | |
| 231 | + | |
| 232 | +void tSquareSampleRateChanged (tSquare* const cy) | |
| 233 | +{ | |
| 234 | + _tSquare* c = *cy; | |
| 235 | + | |
| 236 | + c->inc = c->freq * leaf.invSampleRate; | |
| 237 | +} | |
| 238 | + | |
| 239 | +//===================================================================== | |
| 240 | +// Sawtooth | |
| 241 | +void tSawtooth_init(tSawtooth* const cy) | |
| 242 | +{ | |
| 243 | + tSawtooth_initToPool(cy, &leaf.mempool); | |
| 244 | +} | |
| 245 | + | |
| 246 | +void tSawtooth_free(tSawtooth* const cy) | |
| 247 | +{ | |
| 248 | + tSawtooth_freeFromPool(cy, &leaf.mempool); | |
| 249 | +} | |
| 250 | + | |
| 251 | +void tSawtooth_initToPool (tSawtooth* const cy, tMempool* const mp) | |
| 252 | +{ | |
| 253 | + _tMempool* m = *mp; | |
| 254 | + _tSawtooth* c = *cy = (_tSawtooth*) mpool_alloc(sizeof(_tSawtooth), m); | |
| 255 | + | |
| 256 | + c->inc = 0.0f; | |
| 257 | + c->phase = 0.0f; | |
| 258 | + tSawtooth_setFreq(cy, 220); | |
| 259 | +} | |
| 260 | + | |
| 261 | +void tSawtooth_freeFromPool (tSawtooth* const cy, tMempool* const mp) | |
| 262 | +{ | |
| 263 | + _tMempool* m = *mp; | |
| 264 | + _tSawtooth* c = *cy; | |
| 265 | + | |
| 266 | + mpool_free((char*)c, m); | |
| 267 | +} | |
| 268 | + | |
| 269 | +void tSawtooth_setFreq(tSawtooth* const cy, float freq) | |
| 270 | +{ | |
| 271 | + _tSawtooth* c = *cy; | |
| 272 | + | |
| 273 | + c->freq = freq; | |
| 274 | + | |
| 275 | + c->inc = c->freq * leaf.invSampleRate; | |
| 276 | + | |
| 277 | + c->w = c->freq * INV_20; | |
| 278 | + for (c->oct = 0; c->w > 2.0f; c->oct++) | |
| 279 | + { | |
| 280 | + c->w = 0.5f * c->w; | |
| 281 | + } | |
| 282 | + c->w = 2.0f - c->w; | |
| 283 | +} | |
| 284 | + | |
| 285 | +float tSawtooth_tick(tSawtooth* const cy) | |
| 286 | +{ | |
| 287 | + _tSawtooth* c = *cy; | |
| 288 | + | |
| 289 | + // Phasor increment | |
| 290 | + c->phase += c->inc; | |
| 291 | + while (c->phase >= 1.0f) c->phase -= 1.0f; | |
| 292 | + while (c->phase < 0.0f) c->phase += 1.0f; | |
| 293 | + | |
| 294 | + float out = 0.0f; | |
| 295 | + | |
| 296 | + int idx = (int)(c->phase * SAW_TABLE_SIZE); | |
| 297 | + | |
| 298 | + // Wavetable synthesis | |
| 299 | + out = __leaf_table_sawtooth[c->oct+1][idx] + | |
| 300 | + (__leaf_table_sawtooth[c->oct][idx] - __leaf_table_sawtooth[c->oct+1][idx]) * c->w; | |
| 301 | + | |
| 302 | + return out; | |
| 303 | +} | |
| 304 | + | |
| 305 | +void tSawtoothSampleRateChanged (tSawtooth* const cy) | |
| 306 | +{ | |
| 307 | + _tSawtooth* c = *cy; | |
| 308 | + | |
| 309 | + c->inc = c->freq * leaf.invSampleRate; | |
| 310 | +} | |
| 311 | + | |
| 312 | +//======================================================================== | |
| 313 | +// Sine | |
| 314 | +void tSine_init(tSine* const cy, int size) | |
| 315 | +{ | |
| 316 | + tSine_initToPool(cy, size, &leaf.mempool); | |
| 317 | +} | |
| 318 | + | |
| 319 | +void tSine_free(tSine* const cy) | |
| 320 | +{ | |
| 321 | + tSine_freeFromPool(cy, &leaf.mempool); | |
| 322 | +} | |
| 323 | + | |
| 324 | +void tSine_initToPool (tSine* const cy, int size, tMempool* const mp) | |
| 325 | +{ | |
| 326 | + _tMempool* m = *mp; | |
| 327 | + _tSine* c = *cy = (_tSine*) mpool_alloc(sizeof(_tSine), m); | |
| 328 | + | |
| 329 | + c->size = size; | |
| 330 | + c->sine = (float*) mpool_alloc(sizeof(float) * c->size, m); | |
| 331 | + LEAF_generate_sine(c->sine, size); | |
| 332 | + c->inc = 0.0f; | |
| 333 | + c->phase = 0.0f; | |
| 334 | +} | |
| 335 | + | |
| 336 | +void tSine_freeFromPool (tSine* const cy, tMempool* const mp) | |
| 337 | +{ | |
| 338 | + _tMempool* m = *mp; | |
| 339 | + _tSine* c = *cy; | |
| 340 | + | |
| 341 | + mpool_free((char*)c->sine, m); | |
| 342 | + mpool_free((char*)c, m); | |
| 343 | +} | |
| 344 | + | |
| 345 | +void tSine_setFreq(tSine* const cy, float freq) | |
| 346 | +{ | |
| 347 | + _tSine* c = *cy; | |
| 348 | + | |
| 349 | + c->freq = freq; | |
| 350 | + c->inc = freq * leaf.invSampleRate; | |
| 351 | +} | |
| 352 | + | |
| 353 | +float tSine_tick(tSine* const cy) | |
| 354 | +{ | |
| 355 | + _tSine* c = *cy; | |
| 356 | + float temp; | |
| 357 | + int intPart; | |
| 358 | + float fracPart; | |
| 359 | + float samp0; | |
| 360 | + float samp1; | |
| 361 | + | |
| 362 | + // Phasor increment | |
| 363 | + c->phase += c->inc; | |
| 364 | + while (c->phase >= 1.0f) c->phase -= 1.0f; | |
| 365 | + while (c->phase < 0.0f) c->phase += 1.0f; | |
| 366 | + | |
| 367 | + // Wavetable synthesis | |
| 368 | + | |
| 369 | + temp = c->size * c->phase; | |
| 370 | + intPart = (int)temp; | |
| 371 | + fracPart = temp - (float)intPart; | |
| 372 | + samp0 = c->sine[intPart]; | |
| 373 | + if (++intPart >= c->size) intPart = 0; | |
| 374 | + samp1 = c->sine[intPart]; | |
| 375 | + | |
| 376 | + return (samp0 + (samp1 - samp0) * fracPart); | |
| 377 | + | |
| 378 | +} | |
| 379 | + | |
| 380 | +void tSineSampleRateChanged (tSine* const cy) | |
| 381 | +{ | |
| 382 | + _tSine* c = *cy; | |
| 383 | + | |
| 384 | + c->inc = c->freq * leaf.invSampleRate; | |
| 385 | +} | |
| 386 | + | |
| 387 | +//============================================================================== | |
| 388 | + | |
| 389 | +/* tTri: Anti-aliased Triangle waveform. */ | |
| 390 | +void tTri_init (tTri* const osc) | |
| 391 | +{ | |
| 392 | + tTri_initToPool(osc, &leaf.mempool); | |
| 393 | +} | |
| 394 | + | |
| 395 | +void tTri_free (tTri* const osc) | |
| 396 | +{ | |
| 397 | + tTri_freeFromPool(osc, &leaf.mempool); | |
| 398 | +} | |
| 399 | + | |
| 400 | +void tTri_initToPool (tTri* const osc, tMempool* const mp) | |
| 401 | +{ | |
| 402 | + _tMempool* m = *mp; | |
| 403 | + _tTri* c = *osc = (_tTri*) mpool_alloc(sizeof(_tTri), m); | |
| 404 | + | |
| 405 | + c->inc = 0.0f; | |
| 406 | + c->phase = 0.0f; | |
| 407 | + c->skew = 0.5f; | |
| 408 | + c->lastOut = 0.0f; | |
| 409 | +} | |
| 410 | + | |
| 411 | +void tTri_freeFromPool (tTri* const cy, tMempool* const mp) | |
| 412 | +{ | |
| 413 | + _tMempool* m = *mp; | |
| 414 | + _tTri* c = *cy; | |
| 415 | + | |
| 416 | + mpool_free((char*)c, m); | |
| 417 | +} | |
| 418 | + | |
| 419 | +float tTri_tick (tTri* const osc) | |
| 420 | +{ | |
| 421 | + _tTri* c = *osc; | |
| 422 | + | |
| 423 | + float out; | |
| 424 | + float skew; | |
| 425 | + | |
| 426 | + if (c->phase < c->skew) | |
| 427 | + { | |
| 428 | + out = 1.0f; | |
| 429 | + skew = (1.0f - c->skew) * 2.0f; | |
| 430 | + } | |
| 431 | + else | |
| 432 | + { | |
| 433 | + out = -1.0f; | |
| 434 | + skew = c->skew * 2.0f; | |
| 435 | + } | |
| 436 | + | |
| 437 | + out += LEAF_poly_blep(c->phase, c->inc); | |
| 438 | + out -= LEAF_poly_blep(fmod(c->phase + (1.0f - c->skew), 1.0f), c->inc); | |
| 439 | + | |
| 440 | + out = (skew * c->inc * out) + ((1 - c->inc) * c->lastOut); | |
| 441 | + c->lastOut = out; | |
| 442 | + | |
| 443 | + c->phase += c->inc; | |
| 444 | + if (c->phase >= 1.0f) | |
| 445 | + { | |
| 446 | + c->phase -= 1.0f; | |
| 447 | + } | |
| 448 | + | |
| 449 | + return out; | |
| 450 | +} | |
| 451 | + | |
| 452 | +void tTri_setFreq (tTri* const osc, float freq) | |
| 453 | +{ | |
| 454 | + _tTri* c = *osc; | |
| 455 | + | |
| 456 | + c->freq = freq; | |
| 457 | + c->inc = freq * leaf.invSampleRate; | |
| 458 | +} | |
| 459 | + | |
| 460 | +void tTri_setSkew (tTri* const osc, float skew) | |
| 461 | +{ | |
| 462 | + _tTri* c = *osc; | |
| 463 | + c->skew = (skew + 1.0f) * 0.5f; | |
| 464 | +} | |
| 465 | + | |
| 466 | + | |
| 467 | +//============================================================================== | |
| 468 | + | |
| 469 | +/* tPulse: Anti-aliased pulse waveform. */ | |
| 470 | +void tPulse_init (tPulse* const osc) | |
| 471 | +{ | |
| 472 | + tPulse_initToPool(osc, &leaf.mempool); | |
| 473 | +} | |
| 474 | + | |
| 475 | +void tPulse_free (tPulse* const osc) | |
| 476 | +{ | |
| 477 | + tPulse_freeFromPool(osc, &leaf.mempool); | |
| 478 | +} | |
| 479 | + | |
| 480 | +void tPulse_initToPool (tPulse* const osc, tMempool* const mp) | |
| 481 | +{ | |
| 482 | + _tMempool* m = *mp; | |
| 483 | + _tPulse* c = *osc = (_tPulse*) mpool_alloc(sizeof(_tPulse), m); | |
| 484 | + | |
| 485 | + c->inc = 0.0f; | |
| 486 | + c->phase = 0.0f; | |
| 487 | + c->width = 0.5f; | |
| 488 | +} | |
| 489 | + | |
| 490 | +void tPulse_freeFromPool(tPulse* const osc, tMempool* const mp) | |
| 491 | +{ | |
| 492 | + _tMempool* m = *mp; | |
| 493 | + _tPulse* c = *osc; | |
| 494 | + | |
| 495 | + mpool_free((char*)c, m); | |
| 496 | +} | |
| 497 | + | |
| 498 | +float tPulse_tick (tPulse* const osc) | |
| 499 | +{ | |
| 500 | + _tPulse* c = *osc; | |
| 501 | + | |
| 502 | + float out; | |
| 503 | + if (c->phase < c->width) out = 1.0f; | |
| 504 | + else out = -1.0f; | |
| 505 | + out += LEAF_poly_blep(c->phase, c->inc); | |
| 506 | + out -= LEAF_poly_blep(fmod(c->phase + (1.0f - c->width), 1.0f), c->inc); | |
| 507 | + | |
| 508 | + c->phase += c->inc; | |
| 509 | + if (c->phase >= 1.0f) | |
| 510 | + { | |
| 511 | + c->phase -= 1.0f; | |
| 512 | + } | |
| 513 | + | |
| 514 | + return out; | |
| 515 | +} | |
| 516 | + | |
| 517 | +void tPulse_setFreq (tPulse* const osc, float freq) | |
| 518 | +{ | |
| 519 | + _tPulse* c = *osc; | |
| 520 | + | |
| 521 | + c->freq = freq; | |
| 522 | + c->inc = freq * leaf.invSampleRate; | |
| 523 | +} | |
| 524 | + | |
| 525 | +void tPulse_setWidth (tPulse* const osc, float width) | |
| 526 | +{ | |
| 527 | + _tPulse* c = *osc; | |
| 528 | + c->width = width; | |
| 529 | +} | |
| 530 | + | |
| 531 | + | |
| 532 | +//============================================================================== | |
| 533 | + | |
| 534 | +/* tSawtooth: Anti-aliased Sawtooth waveform. */ | |
| 535 | +void tSaw_init (tSaw* const osc) | |
| 536 | +{ | |
| 537 | + tSaw_initToPool(osc, &leaf.mempool); | |
| 538 | +} | |
| 539 | + | |
| 540 | +void tSaw_free (tSaw* const osc) | |
| 541 | +{ | |
| 542 | + tSaw_freeFromPool(osc, &leaf.mempool); | |
| 543 | +} | |
| 544 | + | |
| 545 | +void tSaw_initToPool (tSaw* const osc, tMempool* const mp) | |
| 546 | +{ | |
| 547 | + _tMempool* m = *mp; | |
| 548 | + _tSaw* c = *osc = (_tSaw*) mpool_alloc(sizeof(_tSaw), m); | |
| 549 | + | |
| 550 | + c->inc = 0.0f; | |
| 551 | + c->phase = 0.0f; | |
| 552 | +} | |
| 553 | + | |
| 554 | +void tSaw_freeFromPool (tSaw* const osc, tMempool* const mp) | |
| 555 | +{ | |
| 556 | + _tMempool* m = *mp; | |
| 557 | + _tSaw* c = *osc; | |
| 558 | + | |
| 559 | + mpool_free((char*)c, m); | |
| 560 | +} | |
| 561 | + | |
| 562 | +float tSaw_tick (tSaw* const osc) | |
| 563 | +{ | |
| 564 | + _tSaw* c = *osc; | |
| 565 | + | |
| 566 | + float out = (c->phase * 2.0f) - 1.0f; | |
| 567 | + out -= LEAF_poly_blep(c->phase, c->inc); | |
| 568 | + | |
| 569 | + c->phase += c->inc; | |
| 570 | + if (c->phase >= 1.0f) | |
| 571 | + { | |
| 572 | + c->phase -= 1.0f; | |
| 573 | + } | |
| 574 | + | |
| 575 | + return out; | |
| 576 | +} | |
| 577 | + | |
| 578 | +void tSaw_setFreq (tSaw* const osc, float freq) | |
| 579 | +{ | |
| 580 | + _tSaw* c = *osc; | |
| 581 | + | |
| 582 | + c->freq = freq; | |
| 583 | + | |
| 584 | + c->inc = freq * leaf.invSampleRate; | |
| 585 | +} | |
| 586 | + | |
| 587 | +//======================================================================== | |
| 588 | +/* Phasor */ | |
| 589 | +void tPhasorSampleRateChanged (tPhasor* const ph) | |
| 590 | +{ | |
| 591 | + _tPhasor* p = *ph; | |
| 592 | + | |
| 593 | + p->inc = p->freq * leaf.invSampleRate; | |
| 594 | +}; | |
| 595 | + | |
| 596 | +void tPhasor_init(tPhasor* const ph) | |
| 597 | +{ | |
| 598 | + tPhasor_initToPool(ph, &leaf.mempool); | |
| 599 | +} | |
| 600 | + | |
| 601 | +void tPhasor_free(tPhasor* const ph) | |
| 602 | +{ | |
| 603 | + tPhasor_freeFromPool(ph, &leaf.mempool); | |
| 604 | +} | |
| 605 | + | |
| 606 | +void tPhasor_initToPool (tPhasor* const ph, tMempool* const mp) | |
| 607 | +{ | |
| 608 | + _tMempool* m = *mp; | |
| 609 | + _tPhasor* p = *ph = (_tPhasor*) mpool_alloc(sizeof(_tPhasor), m); | |
| 610 | + | |
| 611 | + p->phase = 0.0f; | |
| 612 | + p->inc = 0.0f; | |
| 613 | + p->phaseDidReset = 0; | |
| 614 | +} | |
| 615 | + | |
| 616 | +void tPhasor_freeFromPool(tPhasor* const ph, tMempool* const mp) | |
| 617 | +{ | |
| 618 | + _tMempool* m = *mp; | |
| 619 | + _tPhasor* p = *ph; | |
| 620 | + | |
| 621 | + mpool_free((char*)p, m); | |
| 622 | +} | |
| 623 | + | |
| 624 | +void tPhasor_setFreq(tPhasor* const ph, float freq) | |
| 625 | +{ | |
| 626 | + _tPhasor* p = *ph; | |
| 627 | + | |
| 628 | + p->freq = freq; | |
| 629 | + | |
| 630 | + p->inc = freq * leaf.invSampleRate; | |
| 631 | +} | |
| 632 | + | |
| 633 | +float tPhasor_tick(tPhasor* const ph) | |
| 634 | +{ | |
| 635 | + _tPhasor* p = *ph; | |
| 636 | + | |
| 637 | + p->phase += p->inc; | |
| 638 | + | |
| 639 | + | |
| 640 | + p->phaseDidReset = 0; | |
| 641 | + if (p->phase >= 1.0f) | |
| 642 | + { | |
| 643 | + p->phaseDidReset = 1; | |
| 644 | + p->phase -= 1.0f; | |
| 645 | + } | |
| 646 | + | |
| 647 | + return p->phase; | |
| 648 | +} | |
| 649 | + | |
| 650 | +/* Noise */ | |
| 651 | +void tNoise_init(tNoise* const ns, NoiseType type) | |
| 652 | +{ | |
| 653 | + tNoise_initToPool(ns, type, &leaf.mempool); | |
| 654 | +} | |
| 655 | + | |
| 656 | +void tNoise_free(tNoise* const ns) | |
| 657 | +{ | |
| 658 | + tNoise_freeFromPool(ns, &leaf.mempool); | |
| 659 | +} | |
| 660 | + | |
| 661 | +void tNoise_initToPool (tNoise* const ns, NoiseType type, tMempool* const mp) | |
| 662 | +{ | |
| 663 | + _tMempool* m = *mp; | |
| 664 | + _tNoise* n = *ns = (_tNoise*) mpool_alloc(sizeof(_tNoise), m); | |
| 665 | + | |
| 666 | + n->type = type; | |
| 667 | + n->rand = leaf.random; | |
| 668 | +} | |
| 669 | + | |
| 670 | +void tNoise_freeFromPool (tNoise* const ns, tMempool* const mp) | |
| 671 | +{ | |
| 672 | + _tMempool* m = *mp; | |
| 673 | + _tNoise* n = *ns; | |
| 674 | + | |
| 675 | + mpool_free((char*)n, m); | |
| 676 | +} | |
| 677 | + | |
| 678 | +float tNoise_tick(tNoise* const ns) | |
| 679 | +{ | |
| 680 | + _tNoise* n = *ns; | |
| 681 | + | |
| 682 | + float rand = (n->rand() * 2.0f) - 1.0f; | |
| 683 | + | |
| 684 | + if (n->type == PinkNoise) | |
| 685 | + { | |
| 686 | + float tmp; | |
| 687 | + n->pinkb0 = 0.99765f * n->pinkb0 + rand * 0.0990460f; | |
| 688 | + n->pinkb1 = 0.96300f * n->pinkb1 + rand * 0.2965164f; | |
| 689 | + n->pinkb2 = 0.57000f * n->pinkb2 + rand * 1.0526913f; | |
| 690 | + tmp = n->pinkb0 + n->pinkb1 + n->pinkb2 + rand * 0.1848f; | |
| 691 | + return (tmp * 0.05f); | |
| 692 | + } | |
| 693 | + else // WhiteNoise | |
| 694 | + { | |
| 695 | + return rand; | |
| 696 | + } | |
| 697 | +} | |
| 698 | + | |
| 699 | +//================================================================================= | |
| 700 | +/* Neuron */ | |
| 701 | + | |
| 702 | +void tNeuronSampleRateChanged(tNeuron* nr) | |
| 703 | +{ | |
| 704 | + | |
| 705 | +} | |
| 706 | + | |
| 707 | +void tNeuron_init(tNeuron* const nr) | |
| 708 | +{ | |
| 709 | + tNeuron_initToPool(nr, &leaf.mempool); | |
| 710 | +} | |
| 711 | + | |
| 712 | +void tNeuron_free(tNeuron* const nr) | |
| 713 | +{ | |
| 714 | + tNeuron_freeFromPool(nr, &leaf.mempool); | |
| 715 | +} | |
| 716 | + | |
| 717 | +void tNeuron_initToPool (tNeuron* const nr, tMempool* const mp) | |
| 718 | +{ | |
| 719 | + _tMempool* m = *mp; | |
| 720 | + _tNeuron* n = *nr = (_tNeuron*) mpool_alloc(sizeof(_tNeuron), m); | |
| 721 | + | |
| 722 | + tPoleZero_initToPool(&n->f, mp); | |
| 723 | + | |
| 724 | + tPoleZero_setBlockZero(&n->f, 0.99f); | |
| 725 | + | |
| 726 | + n->timeStep = 1.0f / 50.0f; | |
| 727 | + | |
| 728 | + n->current = 0.0f; // 100.0f for sound | |
| 729 | + n->voltage = 0.0f; | |
| 730 | + | |
| 731 | + n->mode = NeuronNormal; | |
| 732 | + | |
| 733 | + n->P[0] = 0.0f; | |
| 734 | + n->P[1] = 0.0f; | |
| 735 | + n->P[2] = 1.0f; | |
| 736 | + | |
| 737 | + n->V[0] = -12.0f; | |
| 738 | + n->V[1] = 115.0f; | |
| 739 | + n->V[2] = 10.613f; | |
| 740 | + | |
| 741 | + n->gK = 36.0f; | |
| 742 | + n->gN = 120.0f; | |
| 743 | + n->gL = 0.3f; | |
| 744 | + n->C = 1.0f; | |
| 745 | + | |
| 746 | + n->rate[2] = n->gL/n->C; | |
| 747 | +} | |
| 748 | + | |
| 749 | +void tNeuron_freeFromPool(tNeuron* const nr, tMempool* const mp) | |
| 750 | +{ | |
| 751 | + _tMempool* m = *mp; | |
| 752 | + _tNeuron* n = *nr; | |
| 753 | + | |
| 754 | + tPoleZero_free(&n->f); | |
| 755 | + mpool_free((char*)n, m); | |
| 756 | +} | |
| 757 | + | |
| 758 | +void tNeuron_reset(tNeuron* const nr) | |
| 759 | +{ | |
| 760 | + _tNeuron* n = *nr; | |
| 761 | + | |
| 762 | + tPoleZero_setBlockZero(&n->f, 0.99f); | |
| 763 | + | |
| 764 | + n->timeStep = 1.0f / 50.0f; | |
| 765 | + | |
| 766 | + n->current = 0.0f; // 100.0f for sound | |
| 767 | + n->voltage = 0.0f; | |
| 768 | + | |
| 769 | + n->mode = NeuronNormal; | |
| 770 | + | |
| 771 | + n->P[0] = 0.0f; | |
| 772 | + n->P[1] = 0.0f; | |
| 773 | + n->P[2] = 1.0f; | |
| 774 | + | |
| 775 | + n->V[0] = -12.0f; | |
| 776 | + n->V[1] = 115.0f; | |
| 777 | + n->V[2] = 10.613f; | |
| 778 | + | |
| 779 | + n->gK = 36.0f; | |
| 780 | + n->gN = 120.0f; | |
| 781 | + n->gL = 0.3f; | |
| 782 | + n->C = 1.0f; | |
| 783 | + | |
| 784 | + n->rate[2] = n->gL/n->C; | |
| 785 | +} | |
| 786 | + | |
| 787 | +void tNeuron_setV1(tNeuron* const nr, float V1) | |
| 788 | +{ | |
| 789 | + _tNeuron* n = *nr; | |
| 790 | + n->V[0] = V1; | |
| 791 | +} | |
| 792 | + | |
| 793 | + | |
| 794 | +void tNeuron_setV2(tNeuron* const nr, float V2) | |
| 795 | +{ | |
| 796 | + _tNeuron* n = *nr; | |
| 797 | + n->V[1] = V2; | |
| 798 | +} | |
| 799 | + | |
| 800 | +void tNeuron_setV3(tNeuron* const nr, float V3) | |
| 801 | +{ | |
| 802 | + _tNeuron* n = *nr; | |
| 803 | + n->V[2] = V3; | |
| 804 | +} | |
| 805 | + | |
| 806 | +void tNeuron_setTimeStep(tNeuron* const nr, float timeStep) | |
| 807 | +{ | |
| 808 | + _tNeuron* n = *nr; | |
| 809 | + n->timeStep = timeStep; | |
| 810 | +} | |
| 811 | + | |
| 812 | +void tNeuron_setK(tNeuron* const nr, float K) | |
| 813 | +{ | |
| 814 | + _tNeuron* n = *nr; | |
| 815 | + n->gK = K; | |
| 816 | +} | |
| 817 | + | |
| 818 | +void tNeuron_setL(tNeuron* const nr, float L) | |
| 819 | +{ | |
| 820 | + _tNeuron* n = *nr; | |
| 821 | + n->gL = L; | |
| 822 | + n->rate[2] = n->gL/n->C; | |
| 823 | +} | |
| 824 | + | |
| 825 | +void tNeuron_setN(tNeuron* const nr, float N) | |
| 826 | +{ | |
| 827 | + _tNeuron* n = *nr; | |
| 828 | + n->gN = N; | |
| 829 | +} | |
| 830 | + | |
| 831 | +void tNeuron_setC(tNeuron* const nr, float C) | |
| 832 | +{ | |
| 833 | + _tNeuron* n = *nr; | |
| 834 | + n->C = C; | |
| 835 | + n->rate[2] = n->gL/n->C; | |
| 836 | +} | |
| 837 | + | |
| 838 | +float tNeuron_tick(tNeuron* const nr) | |
| 839 | +{ | |
| 840 | + _tNeuron* n = *nr; | |
| 841 | + | |
| 842 | + float output = 0.0f; | |
| 843 | + float voltage = n->voltage; | |
| 844 | + | |
| 845 | + n->alpha[0] = (0.01f * (10.0f - voltage)) / (expf((10.0f - voltage)/10.0f) - 1.0f); | |
| 846 | + n->alpha[1] = (0.1f * (25.0f-voltage)) / (expf((25.0f-voltage)/10.0f) - 1.0f); | |
| 847 | + n->alpha[2] = (0.07f * expf((-1.0f * voltage)/20.0f)); | |
| 848 | + | |
| 849 | + n->beta[0] = (0.125f * expf((-1.0f* voltage)/80.0f)); | |
| 850 | + n->beta[1] = (4.0f * expf((-1.0f * voltage)/18.0f)); | |
| 851 | + n->beta[2] = (1.0f / (expf((30.0f-voltage)/10.0f) + 1.0f)); | |
| 852 | + | |
| 853 | + for (int i = 0; i < 3; i++) | |
| 854 | + { | |
| 855 | + n->P[i] = (n->alpha[i] * n->timeStep) + ((1.0f - ((n->alpha[i] + n->beta[i]) * n->timeStep)) * n->P[i]); | |
| 856 | + | |
| 857 | + if (n->P[i] > 1.0f) n->P[i] = 0.0f; | |
| 858 | + else if (n->P[i] < -1.0f) n->P[i] = 0.0f; | |
| 859 | + } | |
| 860 | + // rate[0]= k ; rate[1] = Na ; rate[2] = l | |
| 861 | + n->rate[0] = ((n->gK * powf(n->P[0], 4.0f)) / n->C); | |
| 862 | + n->rate[1] = ((n->gN * powf(n->P[1], 3.0f) * n->P[2]) / n->C); | |
| 863 | + | |
| 864 | + //calculate the final membrane voltage based on the computed variables | |
| 865 | + n->voltage = voltage + | |
| 866 | + (n->timeStep * n->current / n->C) - | |
| 867 | + (n->timeStep * ( n->rate[0] * (voltage - n->V[0]) + n->rate[1] * (voltage - n->V[1]) + n->rate[2] * (voltage - n->V[2]))); | |
| 868 | + | |
| 869 | + if (n->mode == NeuronTanh) | |
| 870 | + { | |
| 871 | + n->voltage = 100.0f * tanhf(0.01f * n->voltage); | |
| 872 | + } | |
| 873 | + else if (n->mode == NeuronAaltoShaper) | |
| 874 | + { | |
| 875 | + float shapeVoltage = 0.01f * n->voltage; | |
| 876 | + | |
| 877 | + float w, c, xc, xc2, xc4; | |
| 878 | + | |
| 879 | + float sqrt8 = 2.82842712475f; | |
| 880 | + | |
| 881 | + float wscale = 1.30612244898f; | |
| 882 | + float m_drive = 1.0f; | |
| 883 | + | |
| 884 | + xc = LEAF_clip(-sqrt8, shapeVoltage, sqrt8); | |
| 885 | + | |
| 886 | + xc2 = xc*xc; | |
| 887 | + | |
| 888 | + c = 0.5f * shapeVoltage * (3.0f - (xc2)); | |
| 889 | + | |
| 890 | + xc4 = xc2 * xc2; | |
| 891 | + | |
| 892 | + w = (1.0f - xc2 * 0.25f + xc4 * 0.015625f) * wscale; | |
| 893 | + | |
| 894 | + shapeVoltage = w * (c + 0.05f * xc2) * (m_drive + 0.75f); | |
| 895 | + | |
| 896 | + n->voltage = 100.0f * shapeVoltage; | |
| 897 | + } | |
| 898 | + | |
| 899 | + | |
| 900 | + if (n->voltage > 100.0f) n->voltage = 100.0f; | |
| 901 | + else if (n->voltage < -100.) n->voltage = -100.0f; | |
| 902 | + | |
| 903 | + //(inputCurrent + (voltage - ((voltage * timeStep) / timeConstant)) + P[0] + P[1] + P[2]) => voltage; | |
| 904 | + // now we should have a result | |
| 905 | + //set the output voltage to the "step" ugen, which controls the DAC. | |
| 906 | + output = n->voltage * 0.01f; // volts | |
| 907 | + | |
| 908 | + output = tPoleZero_tick(&n->f, output); | |
| 909 | + | |
| 910 | + return output; | |
| 911 | + | |
| 912 | +} | |
| 913 | + | |
| 914 | +void tNeuron_setMode (tNeuron* const nr, NeuronMode mode) | |
| 915 | +{ | |
| 916 | + _tNeuron* n = *nr; | |
| 917 | + n->mode = mode; | |
| 918 | +} | |
| 919 | + | |
| 920 | +void tNeuron_setCurrent (tNeuron* const nr, float current) | |
| 921 | +{ | |
| 922 | + _tNeuron* n = *nr; | |
| 923 | + n->current = current; | |
| 924 | +} | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | +void tMinBLEP_init (tMinBLEP* const minblep, int zeroCrossings, int oversamplerRatio) | |
| 929 | +{ | |
| 930 | + tMinBLEP_initToPool(minblep, zeroCrossings, oversamplerRatio, &leaf.mempool); | |
| 931 | +} | |
| 932 | + | |
| 933 | +void tMinBLEP_free (tMinBLEP* const minblep) | |
| 934 | +{ | |
| 935 | + tMinBLEP_freeFromPool(minblep, &leaf.mempool); | |
| 936 | +} | |
| 937 | + | |
| 938 | +void tMinBLEP_initToPool (tMinBLEP* const minblep, int zeroCrossings, int oversamplerRatio, tMempool* const mp) | |
| 939 | +{ | |
| 940 | + _tMempool* m = *mp; | |
| 941 | + _tMinBLEP* mb = *minblep = (_tMinBLEP*) mpool_alloc(sizeof(_tMinBLEP), m); | |
| 942 | + | |
| 943 | + mb->overSamplingRatio = zeroCrossings; | |
| 944 | + mb->zeroCrossings = oversamplerRatio; | |
| 945 | + mb->returnDerivative = 0; | |
| 946 | + mb->proportionalBlepFreq = (float) mb->zeroCrossings / (float) mb->overSamplingRatio; // defaults to NyQuist .... | |
| 947 | + | |
| 948 | + mb->lastValue = 0; | |
| 949 | + mb->lastDelta = 0; | |
| 950 | + | |
| 951 | +// float* x1, x2, y1, y2; | |
| 952 | + // AA FILTER | |
| 953 | +// zeromem (coefficients, sizeof (coefficients)); | |
| 954 | + | |
| 955 | + mb->minBlepSize = (mb->zeroCrossings * 2 * mb->overSamplingRatio) + 1; | |
| 956 | + | |
| 957 | + mb->ratio = 1; | |
| 958 | + mb->lastRatio = 1; | |
| 959 | + | |
| 960 | +// createLowPass (ratio); | |
| 961 | +// resetFilters(); | |
| 962 | + | |
| 963 | + mb->blepIndex = 0; | |
| 964 | + mb->numActiveBleps = 0; | |
| 965 | + //currentActiveBlepOffsets; | |
| 966 | + | |
| 967 | + // These probably don't need to be this large | |
| 968 | + mb->offset = (float*) mpool_alloc(sizeof(float) * mb->minBlepSize, m); | |
| 969 | +// mb->freqMultiple = (float*) mpool_alloc(sizeof(float) * mb->minBlepSize, m); | |
| 970 | + mb->pos_change_magnitude = (float*) mpool_alloc(sizeof(float) * mb->minBlepSize, m); | |
| 971 | + mb->vel_change_magnitude = (float*) mpool_alloc(sizeof(float) * mb->minBlepSize, m); | |
| 972 | + | |
| 973 | + mb->minBlepArray = (float*) mpool_alloc(sizeof(float) * mb->minBlepSize, m); | |
| 974 | + mb->minBlepDerivArray = (float*) mpool_alloc(sizeof(float) * mb->minBlepSize, m); | |
| 975 | + | |
| 976 | + tMinBLEP_buildBLEP(minblep); | |
| 977 | +} | |
| 978 | + | |
| 979 | +void tMinBLEP_freeFromPool (tMinBLEP* const minblep, tMempool* const mp) | |
| 980 | +{ | |
| 981 | + _tMempool* m = *mp; | |
| 982 | + _tMinBLEP* mb = *minblep; | |
| 983 | + | |
| 984 | + mpool_free((char*)mb->offset, m); | |
| 985 | +// mpool_free(mb->freqMultiple, m); | |
| 986 | + mpool_free((char*)mb->pos_change_magnitude, m); | |
| 987 | + mpool_free((char*)mb->vel_change_magnitude, m); | |
| 988 | + | |
| 989 | + mpool_free((char*)mb->minBlepArray, m); | |
| 990 | + mpool_free((char*)mb->minBlepDerivArray, m); | |
| 991 | + | |
| 992 | + mpool_free((char*)mb, m); | |
| 993 | +} | |
| 994 | + | |
| 995 | +// SINC Function | |
| 996 | +static float SINC(float x) | |
| 997 | +{ | |
| 998 | + float pix; | |
| 999 | + | |
| 1000 | + if(x == 0.0f) | |
| 1001 | + return 1.0f; | |
| 1002 | + else | |
| 1003 | + { | |
| 1004 | + pix = PI * x; | |
| 1005 | + return sinf(pix) / pix; | |
| 1006 | + } | |
| 1007 | +} | |
| 1008 | + | |
| 1009 | +// Generate Blackman Window | |
| 1010 | +static void BlackmanWindow(int n, float *w) | |
| 1011 | +{ | |
| 1012 | + int m = n - 1; | |
| 1013 | + int i; | |
| 1014 | + float f1, f2, fm; | |
| 1015 | + | |
| 1016 | + fm = (float)m; | |
| 1017 | + for(i = 0; i <= m; i++) | |
| 1018 | + { | |
| 1019 | + f1 = (2.0f * PI * (float)i) / fm; | |
| 1020 | + f2 = 2.0f * f1; | |
| 1021 | + w[i] = 0.42f - (0.5f * cosf(f1)) + (0.08f * cosf(f2)); | |
| 1022 | + } | |
| 1023 | +} | |
| 1024 | + | |
| 1025 | +// Discrete Fourier Transform | |
| 1026 | +static void DFT(int n, float *realTime, float *imagTime, float *realFreq, float *imagFreq) | |
| 1027 | +{ | |
| 1028 | + int k, i; | |
| 1029 | + float sr, si, p; | |
| 1030 | + | |
| 1031 | + for(k = 0; k < n; k++) | |
| 1032 | + { | |
| 1033 | + realFreq[k] = 0.0f; | |
| 1034 | + imagFreq[k] = 0.0f; | |
| 1035 | + } | |
| 1036 | + | |
| 1037 | + for(k = 0; k < n; k++) | |
| 1038 | + for(i = 0; i < n; i++) | |
| 1039 | + { | |
| 1040 | + p = (2.0f * PI * (float)(k * i)) / n; | |
| 1041 | + sr = cosf(p); | |
| 1042 | + si = -sinf(p); | |
| 1043 | + realFreq[k] += (realTime[i] * sr) - (imagTime[i] * si); | |
| 1044 | + imagFreq[k] += (realTime[i] * si) + (imagTime[i] * sr); | |
| 1045 | + } | |
| 1046 | +} | |
| 1047 | + | |
| 1048 | +// Inverse Discrete Fourier Transform | |
| 1049 | +static void InverseDFT(int n, float *realTime, float *imagTime, float *realFreq, float *imagFreq) | |
| 1050 | +{ | |
| 1051 | + int k, i; | |
| 1052 | + float sr, si, p; | |
| 1053 | + | |
| 1054 | + for(k = 0; k < n; k++) | |
| 1055 | + { | |
| 1056 | + realTime[k] = 0.0f; | |
| 1057 | + imagTime[k] = 0.0f; | |
| 1058 | + } | |
| 1059 | + | |
| 1060 | + for(k = 0; k < n; k++) | |
| 1061 | + { | |
| 1062 | + for(i = 0; i < n; i++) | |
| 1063 | + { | |
| 1064 | + p = (2.0f * PI * (float)(k * i)) / n; | |
| 1065 | + sr = cosf(p); | |
| 1066 | + si = -sinf(p); | |
| 1067 | + realTime[k] += (realFreq[i] * sr) + (imagFreq[i] * si); | |
| 1068 | + imagTime[k] += (realFreq[i] * si) - (imagFreq[i] * sr); | |
| 1069 | + } | |
| 1070 | + realTime[k] /= n; | |
| 1071 | + imagTime[k] /= n; | |
| 1072 | + } | |
| 1073 | +} | |
| 1074 | + | |
| 1075 | +// Complex Absolute Value | |
| 1076 | +static float complexabs(float x, float y) | |
| 1077 | +{ | |
| 1078 | + return sqrtf((x * x) + (y * y)); | |
| 1079 | +} | |
| 1080 | + | |
| 1081 | +// Complex Exponential | |
| 1082 | +static void complexexp(float x, float y, float *zx, float *zy) | |
| 1083 | +{ | |
| 1084 | + float expx; | |
| 1085 | + | |
| 1086 | + expx = expf(x); | |
| 1087 | + *zx = expx * cosf(y); | |
| 1088 | + *zy = expx * sinf(y); | |
| 1089 | +} | |
| 1090 | + | |
| 1091 | +// Compute Real Cepstrum Of Signal | |
| 1092 | +static void RealCepstrum(int n, float *signal, float *realCepstrum) | |
| 1093 | +{ | |
| 1094 | + int i; | |
| 1095 | + | |
| 1096 | + float realTime[n]; | |
| 1097 | + float imagTime[n]; | |
| 1098 | + float realFreq[n]; | |
| 1099 | + float imagFreq[n]; | |
| 1100 | + | |
| 1101 | + // Compose Complex FFT Input | |
| 1102 | + | |
| 1103 | + for(i = 0; i < n; i++) | |
| 1104 | + { | |
| 1105 | + realTime[i] = signal[i]; | |
| 1106 | + imagTime[i] = 0.0f; | |
| 1107 | + } | |
| 1108 | + | |
| 1109 | + // Perform DFT | |
| 1110 | + | |
| 1111 | + DFT(n, realTime, imagTime, realFreq, imagFreq); | |
| 1112 | + | |
| 1113 | + // Calculate Log Of Absolute Value | |
| 1114 | + | |
| 1115 | + for(i = 0; i < n; i++) | |
| 1116 | + { | |
| 1117 | + realFreq[i] = logf(complexabs(realFreq[i], imagFreq[i])); | |
| 1118 | + imagFreq[i] = 0.0f; | |
| 1119 | + } | |
| 1120 | + | |
| 1121 | + // Perform Inverse FFT | |
| 1122 | + | |
| 1123 | + InverseDFT(n, realTime, imagTime, realFreq, imagFreq); | |
| 1124 | + | |
| 1125 | + // Output Real Part Of FFT | |
| 1126 | + for(i = 0; i < n; i++) | |
| 1127 | + realCepstrum[i] = realTime[i]; | |
| 1128 | +} | |
| 1129 | + | |
| 1130 | +// Compute Minimum Phase Reconstruction Of Signal | |
| 1131 | +static void MinimumPhase(int n, float *realCepstrum, float *minimumPhase) | |
| 1132 | +{ | |
| 1133 | + int i, nd2; | |
| 1134 | + float realTime[n]; | |
| 1135 | + float imagTime[n]; | |
| 1136 | + float realFreq[n]; | |
| 1137 | + float imagFreq[n]; | |
| 1138 | + | |
| 1139 | + nd2 = n / 2; | |
| 1140 | + | |
| 1141 | + if((n % 2) == 1) | |
| 1142 | + { | |
| 1143 | + realTime[0] = realCepstrum[0]; | |
| 1144 | + for(i = 1; i < nd2; i++) | |
| 1145 | + realTime[i] = 2.0f * realCepstrum[i]; | |
| 1146 | + for(i = nd2; i < n; i++) | |
| 1147 | + realTime[i] = 0.0f; | |
| 1148 | + } | |
| 1149 | + else | |
| 1150 | + { | |
| 1151 | + realTime[0] = realCepstrum[0]; | |
| 1152 | + for(i = 1; i < nd2; i++) | |
| 1153 | + realTime[i] = 2.0f * realCepstrum[i]; | |
| 1154 | + realTime[nd2] = realCepstrum[nd2]; | |
| 1155 | + for(i = nd2 + 1; i < n; i++) | |
| 1156 | + realTime[i] = 0.0f; | |
| 1157 | + } | |
| 1158 | + | |
| 1159 | + for(i = 0; i < n; i++) | |
| 1160 | + imagTime[i] = 0.0f; | |
| 1161 | + | |
| 1162 | + DFT(n, realTime, imagTime, realFreq, imagFreq); | |
| 1163 | + | |
| 1164 | + for(i = 0; i < n; i++) | |
| 1165 | + complexexp(realFreq[i], imagFreq[i], &realFreq[i], &imagFreq[i]); | |
| 1166 | + | |
| 1167 | + InverseDFT(n, realTime, imagTime, realFreq, imagFreq); | |
| 1168 | + | |
| 1169 | + for(i = 0; i < n; i++) | |
| 1170 | + minimumPhase[i] = realTime[i]; | |
| 1171 | +} | |
| 1172 | + | |
| 1173 | + | |
| 1174 | +// Generate the MinBLEP | |
| 1175 | +void tMinBLEP_buildBLEP(tMinBLEP* const minblep) | |
| 1176 | +{ | |
| 1177 | + _tMinBLEP* m = *minblep; | |
| 1178 | + | |
| 1179 | + int i, n; | |
| 1180 | + float r, a, b; | |
| 1181 | + | |
| 1182 | + n = m->minBlepSize; | |
| 1183 | + | |
| 1184 | + // Generate Sinc | |
| 1185 | + | |
| 1186 | + a = -m->overSamplingRatio; | |
| 1187 | + b = m->overSamplingRatio; | |
| 1188 | + for(i = 0; i < n; i++) | |
| 1189 | + { | |
| 1190 | + r = ((float)i) / ((float)(n - 1)); | |
| 1191 | + m->minBlepArray[i] = SINC(a + (r * (b - a))); | |
| 1192 | + m->minBlepDerivArray[i] = 0; | |
| 1193 | + } | |
| 1194 | + | |
| 1195 | + // Window Sinc | |
| 1196 | + | |
| 1197 | + BlackmanWindow(n, m->minBlepDerivArray); | |
| 1198 | + for(i = 0; i < n; i++) | |
| 1199 | + m->minBlepArray[i] *= m->minBlepDerivArray[i]; | |
| 1200 | + | |
| 1201 | + // Minimum Phase Reconstruction | |
| 1202 | + | |
| 1203 | + RealCepstrum(n, m->minBlepArray, m->minBlepDerivArray); | |
| 1204 | + MinimumPhase(n, m->minBlepDerivArray, m->minBlepArray); | |
| 1205 | + | |
| 1206 | + // Integrate Into MinBLEP | |
| 1207 | + a = 0.0f; | |
| 1208 | + float secondInt = 0.0f; | |
| 1209 | + for(i = 0; i < n; i++) | |
| 1210 | + { | |
| 1211 | + a += m->minBlepArray[i]; | |
| 1212 | + m->minBlepArray[i] = a; | |
| 1213 | + | |
| 1214 | + secondInt += a; | |
| 1215 | + m->minBlepDerivArray[i] = secondInt; | |
| 1216 | + } | |
| 1217 | + | |
| 1218 | + // Normalize | |
| 1219 | + a = m->minBlepArray[n - 1]; | |
| 1220 | + a = 1.0f / a; | |
| 1221 | + b = 0.0f; | |
| 1222 | + for(i = 0; i < n; i++) | |
| 1223 | + { | |
| 1224 | + m->minBlepArray[i] *= a; | |
| 1225 | + b = fmaxf(b, m->minBlepDerivArray[i]); | |
| 1226 | + } | |
| 1227 | + | |
| 1228 | + // Normalize ... | |
| 1229 | + b = 1.0f/b; | |
| 1230 | + for (i = 0; i < n; i++) | |
| 1231 | + { | |
| 1232 | + m->minBlepDerivArray[i] *= b; | |
| 1233 | + m->minBlepDerivArray[i] -= ((float)i) / ((float) n-1); | |
| 1234 | + | |
| 1235 | + // SUBTRACT 1 and invert so the signal (so it goes 1->0) | |
| 1236 | + m->minBlepArray[i] -= 1.0f; | |
| 1237 | + m->minBlepArray[i] = -m->minBlepArray[i]; | |
| 1238 | + } | |
| 1239 | +} | |
| 1240 | + | |
| 1241 | +void tMinBLEP_addBLEP (tMinBLEP* const minblep, float offset, float posChange, float velChange) | |
| 1242 | +{ | |
| 1243 | + _tMinBLEP* m = *minblep; | |
| 1244 | + | |
| 1245 | + int n = m->minBlepSize; | |
| 1246 | + | |
| 1247 | + m->offset[m->blepIndex] = offset; | |
| 1248 | +// m->freqMultiple[m->blepIndex] = m->overSamplingRatio*m->proportionalBlepFreq; | |
| 1249 | + m->pos_change_magnitude[m->blepIndex] = posChange; | |
| 1250 | + m->vel_change_magnitude[m->blepIndex] = velChange; | |
| 1251 | + | |
| 1252 | + m->blepIndex++; | |
| 1253 | + if (m->blepIndex >= n) m->blepIndex = 0; | |
| 1254 | + | |
| 1255 | + m->numActiveBleps++; | |
| 1256 | +} | |
| 1257 | + | |
| 1258 | +float tMinBLEP_tick (tMinBLEP* const minblep, float input) | |
| 1259 | +{ | |
| 1260 | + _tMinBLEP* m = *minblep; | |
| 1261 | + // PROCESS ALL BLEPS - | |
| 1262 | + /// for each offset, copy a portion of the blep array to the output .... | |
| 1263 | + | |
| 1264 | + float sample = input; | |
| 1265 | + | |
| 1266 | + int n = m->minBlepSize; | |
| 1267 | + | |
| 1268 | + for (int blep = 1; blep <= m->numActiveBleps; blep++) | |
| 1269 | + { | |
| 1270 | + int i = (m->blepIndex - blep + n) % n; | |
| 1271 | + float adjusted_Freq = m->overSamplingRatio*m->proportionalBlepFreq;//m->freqMultiple[i]; | |
| 1272 | + float exactPosition = m->offset[i]; | |
| 1273 | + | |
| 1274 | + float blepPosExact = adjusted_Freq*(exactPosition + 1); // +1 because this needs to trigger on the LOW SAMPLE | |
| 1275 | + float blepPosSample = 0; | |
| 1276 | + float fraction = modff(blepPosExact, &blepPosSample); | |
| 1277 | + | |
| 1278 | + // LIMIT the scaling on the derivative array | |
| 1279 | + // otherwise, it can get TOO large | |
| 1280 | + float depthLimited = m->proportionalBlepFreq; //jlimit<double>(.1, 1, proportionalBlepFreq); | |
| 1281 | + float blepDeriv_PosExact = depthLimited*m->overSamplingRatio*(exactPosition + 1); | |
| 1282 | + float blepDeriv_Sample = 0; | |
| 1283 | + float fraction_Deriv = modff(blepDeriv_PosExact, &blepDeriv_Sample); | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + // DONE ... we reached the end ... | |
| 1287 | + if (((int) blepPosExact > n) && ((int) blepDeriv_PosExact > n)) | |
| 1288 | + break; | |
| 1289 | + | |
| 1290 | + // BLEP has not yet occurred ... | |
| 1291 | + if (blepPosExact < 0) | |
| 1292 | + continue; | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + // 0TH ORDER COMPENSATION :::: | |
| 1296 | + /// add the BLEP to compensate for discontinuties in the POSITION | |
| 1297 | + if ( fabs(m->pos_change_magnitude[i]) > 0 && blepPosSample < n) | |
| 1298 | + { | |
| 1299 | + // LINEAR INTERPOLATION :::: | |
| 1300 | + float lowValue = m->minBlepArray[(int) blepPosSample]; | |
| 1301 | + float hiValue = lowValue; | |
| 1302 | + | |
| 1303 | + if ((int) blepPosSample + 1 < n) | |
| 1304 | + hiValue = m->minBlepArray[(int) blepPosSample + 1]; | |
| 1305 | + | |
| 1306 | + float delta = hiValue - lowValue; | |
| 1307 | + float exactValue = lowValue + fraction*delta; | |
| 1308 | + | |
| 1309 | + // SCALE by the discontinuity magnitude | |
| 1310 | + exactValue *= m->pos_change_magnitude[i]; | |
| 1311 | + | |
| 1312 | + // ADD to the thruput | |
| 1313 | + sample += exactValue; | |
| 1314 | + } | |
| 1315 | + | |
| 1316 | + | |
| 1317 | + // 1ST ORDER COMPENSATION :::: | |
| 1318 | + /// add the BLEP DERIVATIVE to compensate for discontinuties in the VELOCITY | |
| 1319 | + if ( fabs(m->vel_change_magnitude[i]) > 0 && blepDeriv_PosExact < n) | |
| 1320 | + { | |
| 1321 | + | |
| 1322 | + // LINEAR INTERPOLATION :::: | |
| 1323 | + double lowValue = m->minBlepDerivArray[(int) blepDeriv_PosExact]; | |
| 1324 | + double hiValue = lowValue; | |
| 1325 | + | |
| 1326 | + if ((int) blepDeriv_PosExact + 1 < n) | |
| 1327 | + hiValue = m->minBlepDerivArray[(int) blepDeriv_PosExact + 1]; | |
| 1328 | + | |
| 1329 | + double delta = hiValue - lowValue; | |
| 1330 | + double exactValue = lowValue + fraction_Deriv*delta; | |
| 1331 | + | |
| 1332 | + // SCALE by the discontinuity magnitude` | |
| 1333 | + exactValue *= m->vel_change_magnitude[i]; | |
| 1334 | + | |
| 1335 | + // ADD to the thruput | |
| 1336 | + sample += exactValue; | |
| 1337 | + | |
| 1338 | + } | |
| 1339 | + | |
| 1340 | + // UPDATE :::: | |
| 1341 | + m->offset[i] += 1.0f; | |
| 1342 | + if (m->offset[i] * adjusted_Freq > n) | |
| 1343 | + { | |
| 1344 | + m->numActiveBleps--; | |
| 1345 | + } | |
| 1346 | + } | |
| 1347 | + return sample; | |
| 1348 | +} | |
| 1349 | + | |
| 1350 | +//============================================================================== | |
| 1351 | + | |
| 1352 | +/* tMBTriangle: Anti-aliased Triangle waveform. */ | |
| 1353 | +void tMBTriangle_init (tMBTriangle* const osc) | |
| 1354 | +{ | |
| 1355 | + tMBTriangle_initToPool(osc, &leaf.mempool); | |
| 1356 | +} | |
| 1357 | + | |
| 1358 | +void tMBTriangle_free (tMBTriangle* const osc) | |
| 1359 | +{ | |
| 1360 | + tMBTriangle_freeFromPool(osc, &leaf.mempool); | |
| 1361 | +} | |
| 1362 | + | |
| 1363 | +void tMBTriangle_initToPool (tMBTriangle* const osc, tMempool* const mp) | |
| 1364 | +{ | |
| 1365 | + _tMempool* m = *mp; | |
| 1366 | + _tMBTriangle* c = *osc = (_tMBTriangle*) mpool_alloc(sizeof(_tMBTriangle), m); | |
| 1367 | + | |
| 1368 | + c->inc = 0.0f; | |
| 1369 | + c->phase = 0.0f; | |
| 1370 | + c->skew = 0.5f; | |
| 1371 | + c->lastOut = 0.0f; | |
| 1372 | + | |
| 1373 | + tMinBLEP_initToPool(&c->minBlep, 16, 32, mp); | |
| 1374 | + tHighpass_initToPool(&c->dcBlock, 5.0f, mp); | |
| 1375 | +} | |
| 1376 | + | |
| 1377 | +void tMBTriangle_freeFromPool (tMBTriangle* const cy, tMempool* const mp) | |
| 1378 | +{ | |
| 1379 | + _tMempool* m = *mp; | |
| 1380 | + _tMBTriangle* c = *cy; | |
| 1381 | + | |
| 1382 | + tMinBLEP_freeFromPool(&c->minBlep, mp); | |
| 1383 | + tHighpass_freeFromPool(&c->dcBlock, mp); | |
| 1384 | + | |
| 1385 | + mpool_free((char*)c, m); | |
| 1386 | +} | |
| 1387 | + | |
| 1388 | +float tMBTriangle_tick (tMBTriangle* const osc) | |
| 1389 | +{ | |
| 1390 | + _tMBTriangle* c = *osc; | |
| 1391 | + | |
| 1392 | + float out; | |
| 1393 | + | |
| 1394 | + c->phase += c->inc; | |
| 1395 | + if (c->phase >= 1.0f) | |
| 1396 | + { | |
| 1397 | + c->phase -= 1.0f; | |
| 1398 | + float offset = 1.0f - ((c->inc - c->phase) / c->inc); | |
| 1399 | + tMinBLEP_addBLEP(&c->minBlep, offset, -2, 0.0f); | |
| 1400 | + } | |
| 1401 | + if (c->skew <= c->phase && c->phase < c->skew + c->inc) | |
| 1402 | + { | |
| 1403 | + float offset = 1.0f - ((c->inc - c->phase + c->skew) / c->inc); | |
| 1404 | + tMinBLEP_addBLEP(&c->minBlep, offset, 2, 0.0f); | |
| 1405 | + } | |
| 1406 | + | |
| 1407 | + if (c->phase < c->skew) | |
| 1408 | + { | |
| 1409 | + out = (1.0f - c->skew) * 2.0f; | |
| 1410 | + } | |
| 1411 | + else | |
| 1412 | + { | |
| 1413 | + out = -c->skew * 2.0f; | |
| 1414 | + } | |
| 1415 | + | |
| 1416 | + out = tMinBLEP_tick(&c->minBlep, out);// - phasor->inc * 2.0f; | |
| 1417 | + | |
| 1418 | + out = (c->inc * out) + ((1 - c->inc) * c->lastOut); | |
| 1419 | + c->lastOut = out; | |
| 1420 | + | |
| 1421 | + return tHighpass_tick(&c->dcBlock, out * 4.0f); | |
| 1422 | + | |
| 1423 | +// float offset; | |
| 1424 | +// float vel = 2; | |
| 1425 | +// c->phase += c->inc; | |
| 1426 | +// if (c->phase >= 1.0f) | |
| 1427 | +// { | |
| 1428 | +// c->phase -= 1.0f; | |
| 1429 | +// offset = 1.0f - ((c->inc - c->phase) / c->inc); | |
| 1430 | +// tMinBLEP_addBLEP(&c->minBlep, offset, 0.0f, -vel); | |
| 1431 | +// } | |
| 1432 | +// if (c->skew <= c->phase && c->phase < c->skew + c->inc) | |
| 1433 | +// { | |
| 1434 | +// offset = 1.0f - ((c->inc - c->phase + c->skew) / c->inc); | |
| 1435 | +// tMinBLEP_addBLEP(&c->minBlep, offset, 0.0f, vel); | |
| 1436 | +// } | |
| 1437 | +// | |
| 1438 | +// float out; | |
| 1439 | +// if (c->phase < c->skew) out = 1.0f - (c->phase / c->skew); | |
| 1440 | +// else out = (c->phase - c->skew) / (1 - c->skew); | |
| 1441 | +// | |
| 1442 | +// out = (out - 0.5f) * 2.0f; | |
| 1443 | +// | |
| 1444 | +//// return tHighpass_tick(&c->dcBlock, tMinBLEP_tick(&c->minBlep, out)); | |
| 1445 | +// return tMinBLEP_tick(&c->minBlep, out); | |
| 1446 | +} | |
| 1447 | + | |
| 1448 | +void tMBTriangle_setFreq (tMBTriangle* const osc, float freq) | |
| 1449 | +{ | |
| 1450 | + _tMBTriangle* c = *osc; | |
| 1451 | + | |
| 1452 | + c->freq = freq; | |
| 1453 | + c->inc = freq * leaf.invSampleRate; | |
| 1454 | + | |
| 1455 | +// tHighpass_setFreq(&c->dcBlock, freq*0.5); | |
| 1456 | +} | |
| 1457 | + | |
| 1458 | +void tMBTriangle_setSkew (tMBTriangle* const osc, float skew) | |
| 1459 | +{ | |
| 1460 | + _tMBTriangle* c = *osc; | |
| 1461 | + c->skew = (skew + 1.0f) * 0.5f; | |
| 1462 | +} | |
| 1463 | + | |
| 1464 | +void tMBTriangle_sync (tMBTriangle* const osc, float phase) | |
| 1465 | +{ | |
| 1466 | + _tMBTriangle* c = *osc; | |
| 1467 | + | |
| 1468 | + phase += 0.5f; | |
| 1469 | + | |
| 1470 | + int intPart = (int) phase; | |
| 1471 | + phase = phase - (float) intPart; | |
| 1472 | + | |
| 1473 | + float before, after; | |
| 1474 | + | |
| 1475 | + if (c->phase < c->skew) before = (1.0f - c->skew - c->phase) * 2.0f; | |
| 1476 | + else before = -(c->skew - c->phase) * 2.0f; | |
| 1477 | + | |
| 1478 | + if (phase < c->skew) after = (1.0f - c->skew - c->phase) * 2.0f; | |
| 1479 | + else after = -(c->skew - c->phase) * 2.0f; | |
| 1480 | + | |
| 1481 | + c->phase = phase; | |
| 1482 | + | |
| 1483 | + float offset = 0.0f;//1.0f - ((c->inc - c->phase) / c->inc); | |
| 1484 | + tMinBLEP_addBLEP(&c->minBlep, offset, before - after, 0.0f); | |
| 1485 | + | |
| 1486 | + if (c->phase < c->skew) c->lastOut = 1.0f - (c->phase / c->skew); | |
| 1487 | + else c->lastOut = (c->phase - c->skew) / (1.0f - c->skew); | |
| 1488 | +} | |
| 1489 | + | |
| 1490 | +//============================================================================== | |
| 1491 | + | |
| 1492 | +/* tMBPulse: Anti-aliased pulse waveform. */ | |
| 1493 | +void tMBPulse_init (tMBPulse* const osc) | |
| 1494 | +{ | |
| 1495 | + tMBPulse_initToPool(osc, &leaf.mempool); | |
| 1496 | +} | |
| 1497 | + | |
| 1498 | +void tMBPulse_free (tMBPulse* const osc) | |
| 1499 | +{ | |
| 1500 | + tMBPulse_freeFromPool(osc, &leaf.mempool); | |
| 1501 | +} | |
| 1502 | + | |
| 1503 | +void tMBPulse_initToPool (tMBPulse* const osc, tMempool* const mp) | |
| 1504 | +{ | |
| 1505 | + _tMempool* m = *mp; | |
| 1506 | + _tMBPulse* c = *osc = (_tMBPulse*) mpool_alloc(sizeof(_tMBPulse), m); | |
| 1507 | + | |
| 1508 | + c->inc = 0.0f; | |
| 1509 | + c->phase = 0.0f; | |
| 1510 | + c->width = 0.5f; | |
| 1511 | + | |
| 1512 | + tMinBLEP_initToPool(&c->minBlep, 16, 32, mp); | |
| 1513 | + tHighpass_initToPool(&c->dcBlock, 10.0f, mp); | |
| 1514 | +} | |
| 1515 | + | |
| 1516 | +void tMBPulse_freeFromPool(tMBPulse* const osc, tMempool* const mp) | |
| 1517 | +{ | |
| 1518 | + _tMempool* m = *mp; | |
| 1519 | + _tMBPulse* c = *osc; | |
| 1520 | + | |
| 1521 | + tMinBLEP_freeFromPool(&c->minBlep, mp); | |
| 1522 | + tHighpass_freeFromPool(&c->dcBlock, mp); | |
| 1523 | + | |
| 1524 | + mpool_free((char*)c, m); | |
| 1525 | +} | |
| 1526 | + | |
| 1527 | +float tMBPulse_tick (tMBPulse* const osc) | |
| 1528 | +{ | |
| 1529 | + _tMBPulse* c = *osc; | |
| 1530 | + | |
| 1531 | + | |
| 1532 | + | |
| 1533 | + c->phase += c->inc; | |
| 1534 | + if (c->phase >= 1.0f) | |
| 1535 | + { | |
| 1536 | + c->phase -= 1.0f; | |
| 1537 | + float offset = 1.0f - ((c->inc - c->phase) / c->inc); | |
| 1538 | + tMinBLEP_addBLEP(&c->minBlep, offset, -2.0f, 0.0f); | |
| 1539 | + } | |
| 1540 | + if (c->width <= c->phase && c->phase < c->width + c->inc) | |
| 1541 | + { | |
| 1542 | + float offset = 1.0f - ((c->inc - c->phase + c->width) / c->inc); | |
| 1543 | + tMinBLEP_addBLEP(&c->minBlep, offset, 2.0f, 0.0f); | |
| 1544 | + } | |
| 1545 | + | |
| 1546 | + float out; | |
| 1547 | + if (c->phase < c->width) out = 1.0f; | |
| 1548 | + else out = -1.0f; | |
| 1549 | + | |
| 1550 | + return tHighpass_tick(&c->dcBlock, tMinBLEP_tick(&c->minBlep, out)); | |
| 1551 | +} | |
| 1552 | + | |
| 1553 | +void tMBPulse_setFreq (tMBPulse* const osc, float freq) | |
| 1554 | +{ | |
| 1555 | + _tMBPulse* c = *osc; | |
| 1556 | + | |
| 1557 | + c->freq = freq; | |
| 1558 | + c->inc = freq * leaf.invSampleRate; | |
| 1559 | +} | |
| 1560 | + | |
| 1561 | +void tMBPulse_setWidth (tMBPulse* const osc, float width) | |
| 1562 | +{ | |
| 1563 | + _tMBPulse* c = *osc; | |
| 1564 | + c->width = width; | |
| 1565 | +} | |
| 1566 | + | |
| 1567 | +void tMBPulse_sync (tMBPulse* const osc, float phase) | |
| 1568 | +{ | |
| 1569 | + _tMBPulse* c = *osc; | |
| 1570 | + int intPart = (int) phase; | |
| 1571 | + phase = phase - (float) intPart; | |
| 1572 | + | |
| 1573 | + float before, after; | |
| 1574 | + | |
| 1575 | + if (c->phase < c->width) before = 1.0f; | |
| 1576 | + else before = -1.0f; | |
| 1577 | + | |
| 1578 | + if (phase < c->width) after = 1.0; | |
| 1579 | + else after = -1.0f; | |
| 1580 | + | |
| 1581 | + float offset = 0.0f;//1.0f - ((c->inc - c->phase) / c->inc); | |
| 1582 | + tMinBLEP_addBLEP(&c->minBlep, offset, before - after, 0.0f); | |
| 1583 | + | |
| 1584 | + c->phase = phase; | |
| 1585 | +} | |
| 1586 | + | |
| 1587 | + | |
| 1588 | +//============================================================================== | |
| 1589 | + | |
| 1590 | +/* tMBSawtooth: Anti-aliased Sawtooth waveform. */ | |
| 1591 | +void tMBSaw_init (tMBSaw* const osc) | |
| 1592 | +{ | |
| 1593 | + tMBSaw_initToPool(osc, &leaf.mempool); | |
| 1594 | +} | |
| 1595 | + | |
| 1596 | +void tMBSaw_free (tMBSaw* const osc) | |
| 1597 | +{ | |
| 1598 | + tMBSaw_freeFromPool(osc, &leaf.mempool); | |
| 1599 | +} | |
| 1600 | + | |
| 1601 | +void tMBSaw_initToPool (tMBSaw* const osc, tMempool* const mp) | |
| 1602 | +{ | |
| 1603 | + _tMempool* m = *mp; | |
| 1604 | + _tMBSaw* c = *osc = (_tMBSaw*) mpool_alloc(sizeof(_tMBSaw), m); | |
| 1605 | + | |
| 1606 | + c->inc = 0.0f; | |
| 1607 | + c->phase = 0.0f; | |
| 1608 | + | |
| 1609 | + tMinBLEP_initToPool(&c->minBlep, 16, 32, mp); | |
| 1610 | + tHighpass_initToPool(&c->dcBlock, 10.0f, mp); | |
| 1611 | +} | |
| 1612 | + | |
| 1613 | +void tMBSaw_freeFromPool (tMBSaw* const osc, tMempool* const mp) | |
| 1614 | +{ | |
| 1615 | + _tMempool* m = *mp; | |
| 1616 | + _tMBSaw* c = *osc; | |
| 1617 | + | |
| 1618 | + tMinBLEP_freeFromPool(&c->minBlep, mp); | |
| 1619 | + tHighpass_freeFromPool(&c->dcBlock, mp); | |
| 1620 | + | |
| 1621 | + mpool_free((char*)c, m); | |
| 1622 | +} | |
| 1623 | + | |
| 1624 | +float tMBSaw_tick (tMBSaw* const osc) | |
| 1625 | +{ | |
| 1626 | + _tMBSaw* c = *osc; | |
| 1627 | + | |
| 1628 | + c->phase += c->inc; | |
| 1629 | + if (c->phase >= 1.0f) | |
| 1630 | + { | |
| 1631 | + c->phase -= 1.0f; | |
| 1632 | + float offset = 1.0f - ((c->inc - c->phase) / c->inc); | |
| 1633 | + tMinBLEP_addBLEP(&c->minBlep, offset, 2.0f, 0.0f); | |
| 1634 | + } | |
| 1635 | + | |
| 1636 | + float out = (c->phase * 2.0f) - 1.0f; | |
| 1637 | + | |
| 1638 | + return tHighpass_tick(&c->dcBlock, tMinBLEP_tick(&c->minBlep, out)); | |
| 1639 | +} | |
| 1640 | + | |
| 1641 | +void tMBSaw_setFreq (tMBSaw* const osc, float freq) | |
| 1642 | +{ | |
| 1643 | + _tMBSaw* c = *osc; | |
| 1644 | + | |
| 1645 | + c->freq = freq; | |
| 1646 | + | |
| 1647 | + c->inc = freq * leaf.invSampleRate; | |
| 1648 | +} | |
| 1649 | + | |
| 1650 | +void tMBSaw_sync (tMBSaw* const osc, float phase) | |
| 1651 | +{ | |
| 1652 | + _tMBSaw* c = *osc; | |
| 1653 | + int intPart = (int) phase; | |
| 1654 | + phase = phase - (float) intPart; | |
| 1655 | + | |
| 1656 | + float offset = 0.0f;//1.0f - ((c->inc - phase +) / c->inc); | |
| 1657 | + tMinBLEP_addBLEP(&c->minBlep, offset, (c->phase - phase) * 2.0f, 0.0f); | |
| 1658 | + | |
| 1659 | + c->phase = phase; | |
| 1660 | +} | |
| @@ -0,0 +1,747 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-string.c | |
| 4 | + Created: 30 Nov 2018 10:41:42am | |
| 5 | + Author: airship | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-physical.h" | |
| 12 | + | |
| 13 | +#else | |
| 14 | + | |
| 15 | +#include "../Inc/leaf-physical.h" | |
| 16 | + | |
| 17 | +#endif | |
| 18 | + | |
| 19 | +/* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ tPluck ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ */ | |
| 20 | +void tPluck_init (tPluck* const pl, float lowestFrequency) | |
| 21 | +{ | |
| 22 | + tPluck_initToPool(pl, lowestFrequency, &leaf.mempool); | |
| 23 | +} | |
| 24 | + | |
| 25 | +void tPluck_free (tPluck* const pl) | |
| 26 | +{ | |
| 27 | + tPluck_freeFromPool(pl, &leaf.mempool); | |
| 28 | +} | |
| 29 | + | |
| 30 | +void tPluck_initToPool (tPluck* const pl, float lowestFrequency, tMempool* const mp) | |
| 31 | +{ | |
| 32 | + _tMempool* m = *mp; | |
| 33 | + _tPluck* p = *pl = (_tPluck*) mpool_alloc(sizeof(_tPluck), m); | |
| 34 | + | |
| 35 | + if ( lowestFrequency <= 0.0f ) lowestFrequency = 10.0f; | |
| 36 | + | |
| 37 | + tNoise_initToPool(&p->noise, WhiteNoise, mp); | |
| 38 | + | |
| 39 | + tOnePole_initToPool(&p->pickFilter, 0.0f, mp); | |
| 40 | + | |
| 41 | + tOneZero_initToPool(&p->loopFilter, 0.0f, mp); | |
| 42 | + | |
| 43 | + tAllpassDelay_initToPool(&p->delayLine, 0.0f, leaf.sampleRate * 2, mp); | |
| 44 | + tAllpassDelay_clear(&p->delayLine); | |
| 45 | + | |
| 46 | + tPluck_setFrequency(pl, 220.0f); | |
| 47 | +} | |
| 48 | + | |
| 49 | +void tPluck_freeFromPool (tPluck* const pl, tMempool* const mp) | |
| 50 | +{ | |
| 51 | + _tMempool* m = *mp; | |
| 52 | + _tPluck* p = *pl; | |
| 53 | + | |
| 54 | + tNoise_freeFromPool(&p->noise, mp); | |
| 55 | + tOnePole_freeFromPool(&p->pickFilter, mp); | |
| 56 | + tOneZero_freeFromPool(&p->loopFilter, mp); | |
| 57 | + tAllpassDelay_freeFromPool(&p->delayLine, mp); | |
| 58 | + | |
| 59 | + mpool_free((char*)p, m); | |
| 60 | +} | |
| 61 | + | |
| 62 | +float tPluck_getLastOut (tPluck* const pl) | |
| 63 | +{ | |
| 64 | + _tPluck* p = *pl; | |
| 65 | + return p->lastOut; | |
| 66 | +} | |
| 67 | + | |
| 68 | +float tPluck_tick (tPluck* const pl) | |
| 69 | +{ | |
| 70 | + _tPluck* p = *pl; | |
| 71 | + return (p->lastOut = 3.0f * tAllpassDelay_tick(&p->delayLine, tOneZero_tick(&p->loopFilter, tAllpassDelay_getLastOut(&p->delayLine) * p->loopGain ) )); | |
| 72 | +} | |
| 73 | + | |
| 74 | +void tPluck_pluck (tPluck* const pl, float amplitude) | |
| 75 | +{ | |
| 76 | + _tPluck* p = *pl; | |
| 77 | + | |
| 78 | + if ( amplitude < 0.0f) amplitude = 0.0f; | |
| 79 | + else if (amplitude > 1.0f) amplitude = 1.0f; | |
| 80 | + | |
| 81 | + tOnePole_setPole(&p->pickFilter, 0.999f - (amplitude * 0.15f)); | |
| 82 | + tOnePole_setGain(&p->pickFilter, amplitude * 0.5f ); | |
| 83 | + | |
| 84 | + // Fill delay with noise additively with current contents. | |
| 85 | + for ( uint32_t i = 0; i < (uint32_t)tAllpassDelay_getDelay(&p->delayLine); i++ ) | |
| 86 | + tAllpassDelay_tick(&p->delayLine, 0.6f * tAllpassDelay_getLastOut(&p->delayLine) + tOnePole_tick(&p->pickFilter, tNoise_tick(&p->noise) ) ); | |
| 87 | +} | |
| 88 | + | |
| 89 | +// Start a note with the given frequency and amplitude.; | |
| 90 | +void tPluck_noteOn (tPluck* const pl, float frequency, float amplitude ) | |
| 91 | +{ | |
| 92 | + _tPluck* p = *pl; | |
| 93 | + p->lastFreq = frequency; | |
| 94 | + tPluck_setFrequency( pl, frequency ); | |
| 95 | + tPluck_pluck( pl, amplitude ); | |
| 96 | +} | |
| 97 | + | |
| 98 | +// Stop a note with the given amplitude (speed of decay). | |
| 99 | +void tPluck_noteOff (tPluck* const pl, float amplitude ) | |
| 100 | +{ | |
| 101 | + _tPluck* p = *pl; | |
| 102 | + | |
| 103 | + if ( amplitude < 0.0f) amplitude = 0.0f; | |
| 104 | + else if (amplitude > 1.0f) amplitude = 1.0f; | |
| 105 | + | |
| 106 | + p->loopGain = 1.0f - amplitude; | |
| 107 | +} | |
| 108 | + | |
| 109 | +// Set instrument parameters for a particular frequency. | |
| 110 | +void tPluck_setFrequency (tPluck* const pl, float frequency ) | |
| 111 | +{ | |
| 112 | + _tPluck* p = *pl; | |
| 113 | + | |
| 114 | + if ( frequency <= 0.0f ) frequency = 0.001f; | |
| 115 | + | |
| 116 | + // Delay = length - filter delay. | |
| 117 | + float delay = ( leaf.sampleRate / frequency ) - tOneZero_getPhaseDelay(&p->loopFilter, frequency ); | |
| 118 | + | |
| 119 | + tAllpassDelay_setDelay(&p->delayLine, delay ); | |
| 120 | + | |
| 121 | + p->loopGain = 0.99f + (frequency * 0.000005f); | |
| 122 | + | |
| 123 | + if ( p->loopGain >= 0.999f ) p->loopGain = 0.999f; | |
| 124 | + | |
| 125 | +} | |
| 126 | + | |
| 127 | +// Perform the control change specified by \e number and \e value (0.0 - 128.0). | |
| 128 | +void tPluck_controlChange (tPluck* const pl, int number, float value) | |
| 129 | +{ | |
| 130 | + return; | |
| 131 | +} | |
| 132 | + | |
| 133 | +void tPluckSampleRateChanged(tPluck* const pl) | |
| 134 | +{ | |
| 135 | + _tPluck* p = *pl; | |
| 136 | + tPluck_setFrequency(pl, p->lastFreq); | |
| 137 | +} | |
| 138 | + | |
| 139 | +/* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ tKarplusStrong ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ */ | |
| 140 | +void tKarplusStrong_init (tKarplusStrong* const pl, float lowestFrequency) | |
| 141 | +{ | |
| 142 | + tKarplusStrong_initToPool(pl, lowestFrequency, &leaf.mempool); | |
| 143 | +} | |
| 144 | + | |
| 145 | +void tKarplusStrong_free (tKarplusStrong* const pl) | |
| 146 | +{ | |
| 147 | + tKarplusStrong_freeFromPool(pl, &leaf.mempool); | |
| 148 | +} | |
| 149 | + | |
| 150 | +void tKarplusStrong_initToPool (tKarplusStrong* const pl, float lowestFrequency, tMempool* const mp) | |
| 151 | +{ | |
| 152 | + _tMempool* m = *mp; | |
| 153 | + _tKarplusStrong* p = *pl = (_tKarplusStrong*) mpool_alloc(sizeof(_tKarplusStrong), m); | |
| 154 | + | |
| 155 | + if ( lowestFrequency <= 0.0f ) lowestFrequency = 8.0f; | |
| 156 | + | |
| 157 | + tAllpassDelay_initToPool(&p->delayLine, 0.0f, leaf.sampleRate * 2, mp); | |
| 158 | + tAllpassDelay_clear(&p->delayLine); | |
| 159 | + | |
| 160 | + tLinearDelay_initToPool(&p->combDelay, 0.0f, leaf.sampleRate * 2, mp); | |
| 161 | + tLinearDelay_clear(&p->combDelay); | |
| 162 | + | |
| 163 | + tOneZero_initToPool(&p->filter, 0.0f, mp); | |
| 164 | + | |
| 165 | + tNoise_initToPool(&p->noise, WhiteNoise, mp); | |
| 166 | + | |
| 167 | + for (int i = 0; i < 4; i++) | |
| 168 | + { | |
| 169 | + tBiQuad_initToPool(&p->biquad[i], mp); | |
| 170 | + } | |
| 171 | + | |
| 172 | + p->pluckAmplitude = 0.3f; | |
| 173 | + p->pickupPosition = 0.4f; | |
| 174 | + | |
| 175 | + p->stretching = 0.9999f; | |
| 176 | + p->baseLoopGain = 0.995f; | |
| 177 | + p->loopGain = 0.999f; | |
| 178 | + | |
| 179 | + tKarplusStrong_setFrequency( pl, 220.0f ); | |
| 180 | +} | |
| 181 | + | |
| 182 | +void tKarplusStrong_freeFromPool (tKarplusStrong* const pl, tMempool* const mp) | |
| 183 | +{ | |
| 184 | + _tMempool* m = *mp; | |
| 185 | + _tKarplusStrong* p = *pl; | |
| 186 | + | |
| 187 | + tAllpassDelay_freeFromPool(&p->delayLine, mp); | |
| 188 | + tLinearDelay_freeFromPool(&p->combDelay, mp); | |
| 189 | + tOneZero_freeFromPool(&p->filter, mp); | |
| 190 | + tNoise_freeFromPool(&p->noise, mp); | |
| 191 | + | |
| 192 | + for (int i = 0; i < 4; i++) | |
| 193 | + { | |
| 194 | + tBiQuad_freeFromPool(&p->biquad[i], mp); | |
| 195 | + } | |
| 196 | + | |
| 197 | + mpool_free((char*)p, m); | |
| 198 | +} | |
| 199 | + | |
| 200 | +float tKarplusStrong_getLastOut (tKarplusStrong* const pl) | |
| 201 | +{ | |
| 202 | + _tKarplusStrong* p = *pl; | |
| 203 | + return p->lastOut; | |
| 204 | +} | |
| 205 | + | |
| 206 | +float tKarplusStrong_tick (tKarplusStrong* const pl) | |
| 207 | +{ | |
| 208 | + _tKarplusStrong* p = *pl; | |
| 209 | + | |
| 210 | + float temp = tAllpassDelay_getLastOut(&p->delayLine) * p->loopGain; | |
| 211 | + | |
| 212 | + // Calculate allpass stretching. | |
| 213 | + for (int i=0; i<4; i++) temp = tBiQuad_tick(&p->biquad[i],temp); | |
| 214 | + | |
| 215 | + // Moving average filter. | |
| 216 | + temp = tOneZero_tick(&p->filter, temp); | |
| 217 | + | |
| 218 | + float out = tAllpassDelay_tick(&p->delayLine, temp); | |
| 219 | + out = out - tLinearDelay_tick(&p->combDelay, out); | |
| 220 | + p->lastOut = out; | |
| 221 | + | |
| 222 | + return p->lastOut; | |
| 223 | +} | |
| 224 | + | |
| 225 | +void tKarplusStrong_pluck (tKarplusStrong* const pl, float amplitude) | |
| 226 | +{ | |
| 227 | + _tKarplusStrong* p = *pl; | |
| 228 | + | |
| 229 | + if ( amplitude < 0.0f) amplitude = 0.0f; | |
| 230 | + else if (amplitude > 1.0f) amplitude = 1.0f; | |
| 231 | + | |
| 232 | + p->pluckAmplitude = amplitude; | |
| 233 | + | |
| 234 | + for ( uint32_t i=0; i < (uint32_t)tAllpassDelay_getDelay(&p->delayLine); i++ ) | |
| 235 | + { | |
| 236 | + // Fill delay with noise additively with current contents. | |
| 237 | + tAllpassDelay_tick(&p->delayLine, (tAllpassDelay_getLastOut(&p->delayLine) * 0.6f) + 0.4f * tNoise_tick(&p->noise) * p->pluckAmplitude ); | |
| 238 | + //delayLine_.tick( combDelay_.tick((delayLine_.lastOut() * 0.6) + 0.4 * noise->tick() * pluckAmplitude_) ); | |
| 239 | + } | |
| 240 | +} | |
| 241 | + | |
| 242 | +// Start a note with the given frequency and amplitude.; | |
| 243 | +void tKarplusStrong_noteOn (tKarplusStrong* const pl, float frequency, float amplitude ) | |
| 244 | +{ | |
| 245 | + tKarplusStrong_setFrequency( pl, frequency ); | |
| 246 | + tKarplusStrong_pluck( pl, amplitude ); | |
| 247 | +} | |
| 248 | + | |
| 249 | +// Stop a note with the given amplitude (speed of decay). | |
| 250 | +void tKarplusStrong_noteOff (tKarplusStrong* const pl, float amplitude ) | |
| 251 | +{ | |
| 252 | + _tKarplusStrong* p = *pl; | |
| 253 | + | |
| 254 | + if ( amplitude < 0.0f) amplitude = 0.0f; | |
| 255 | + else if (amplitude > 1.0f) amplitude = 1.0f; | |
| 256 | + | |
| 257 | + p->loopGain = 1.0f - amplitude; | |
| 258 | +} | |
| 259 | + | |
| 260 | +// Set instrument parameters for a particular frequency. | |
| 261 | +void tKarplusStrong_setFrequency (tKarplusStrong* const pl, float frequency ) | |
| 262 | +{ | |
| 263 | + _tKarplusStrong* p = *pl; | |
| 264 | + | |
| 265 | + if ( frequency <= 0.0f ) frequency = 0.001f; | |
| 266 | + | |
| 267 | + p->lastFrequency = frequency; | |
| 268 | + p->lastLength = leaf.sampleRate / p->lastFrequency; | |
| 269 | + float delay = p->lastLength - 0.5f; | |
| 270 | + tAllpassDelay_setDelay(&p->delayLine, delay); | |
| 271 | + | |
| 272 | + // MAYBE MODIFY LOOP GAINS | |
| 273 | + p->loopGain = p->baseLoopGain + (frequency * 0.000005f); | |
| 274 | + if (p->loopGain >= 1.0f) p->loopGain = 0.99999f; | |
| 275 | + | |
| 276 | + tKarplusStrong_setStretch(pl, p->stretching); | |
| 277 | + | |
| 278 | + tLinearDelay_setDelay(&p->combDelay, 0.5f * p->pickupPosition * p->lastLength ); | |
| 279 | + | |
| 280 | +} | |
| 281 | + | |
| 282 | +// Set the stretch "factor" of the string (0.0 - 1.0). | |
| 283 | +void tKarplusStrong_setStretch (tKarplusStrong* const pl, float stretch ) | |
| 284 | +{ | |
| 285 | + _tKarplusStrong* p = *pl; | |
| 286 | + | |
| 287 | + p->stretching = stretch; | |
| 288 | + float coefficient; | |
| 289 | + float freq = p->lastFrequency * 2.0f; | |
| 290 | + float dFreq = ( (0.5f * leaf.sampleRate) - freq ) * 0.25f; | |
| 291 | + float temp = 0.5f + (stretch * 0.5f); | |
| 292 | + if ( temp > 0.9999f ) temp = 0.9999f; | |
| 293 | + | |
| 294 | + for ( int i=0; i<4; i++ ) | |
| 295 | + { | |
| 296 | + coefficient = temp * temp; | |
| 297 | + tBiQuad_setA2(&p->biquad[i], coefficient); | |
| 298 | + tBiQuad_setB0(&p->biquad[i], coefficient); | |
| 299 | + tBiQuad_setB2(&p->biquad[i], 1.0f); | |
| 300 | + | |
| 301 | + coefficient = -2.0f * temp * cos(TWO_PI * freq / leaf.sampleRate); | |
| 302 | + tBiQuad_setA1(&p->biquad[i], coefficient); | |
| 303 | + tBiQuad_setB1(&p->biquad[i], coefficient); | |
| 304 | + | |
| 305 | + freq += dFreq; | |
| 306 | + } | |
| 307 | +} | |
| 308 | + | |
| 309 | +// Set the pluck or "excitation" position along the string (0.0 - 1.0). | |
| 310 | +void tKarplusStrong_setPickupPosition (tKarplusStrong* const pl, float position ) | |
| 311 | +{ | |
| 312 | + _tKarplusStrong* p = *pl; | |
| 313 | + | |
| 314 | + if (position < 0.0f) p->pickupPosition = 0.0f; | |
| 315 | + else if (position <= 1.0f) p->pickupPosition = position; | |
| 316 | + else p->pickupPosition = 1.0f; | |
| 317 | + | |
| 318 | + tLinearDelay_setDelay(&p->combDelay, 0.5f * p->pickupPosition * p->lastLength); | |
| 319 | +} | |
| 320 | + | |
| 321 | +// Set the base loop gain. | |
| 322 | +void tKarplusStrong_setBaseLoopGain (tKarplusStrong* const pl, float aGain ) | |
| 323 | +{ | |
| 324 | + _tKarplusStrong* p = *pl; | |
| 325 | + | |
| 326 | + p->baseLoopGain = aGain; | |
| 327 | + p->loopGain = p->baseLoopGain + (p->lastFrequency * 0.000005f); | |
| 328 | + if ( p->loopGain > 0.99999f ) p->loopGain = 0.99999f; | |
| 329 | +} | |
| 330 | + | |
| 331 | +// Perform the control change specified by \e number and \e value (0.0 - 128.0). | |
| 332 | +void tKarplusStrong_controlChange (tKarplusStrong* const pl, SKControlType type, float value) | |
| 333 | +{ | |
| 334 | + if ( value < 0.0f ) value = 0.0f; | |
| 335 | + else if (value > 128.0f) value = 128.0f; | |
| 336 | + | |
| 337 | + float normalizedValue = value * INV_128; | |
| 338 | + | |
| 339 | + if (type == SKPickPosition) // 4 | |
| 340 | + tKarplusStrong_setPickupPosition( pl, normalizedValue ); | |
| 341 | + else if (type == SKStringDamping) // 11 | |
| 342 | + tKarplusStrong_setBaseLoopGain( pl, 0.97f + (normalizedValue * 0.03f) ); | |
| 343 | + else if (type == SKDetune) // 1 | |
| 344 | + tKarplusStrong_setStretch( pl, 0.91f + (0.09f * (1.0f - normalizedValue)) ); | |
| 345 | +} | |
| 346 | + | |
| 347 | +void tKarplusStrongSampleRateChanged (tKarplusStrong* const pl) | |
| 348 | +{ | |
| 349 | + _tKarplusStrong* p = *pl; | |
| 350 | + | |
| 351 | + tKarplusStrong_setFrequency(pl, p->lastFrequency); | |
| 352 | + tKarplusStrong_setStretch(pl, p->stretching); | |
| 353 | +} | |
| 354 | + | |
| 355 | +/* Simple Living String*/ | |
| 356 | + | |
| 357 | +void tSimpleLivingString_init(tSimpleLivingString* const pl, float freq, float dampFreq, | |
| 358 | + float decay, float targetLev, float levSmoothFactor, | |
| 359 | + float levStrength, int levMode) | |
| 360 | +{ | |
| 361 | + tSimpleLivingString_initToPool(pl, freq, dampFreq, decay, targetLev, levSmoothFactor, levStrength, levMode, &leaf.mempool); | |
| 362 | +} | |
| 363 | + | |
| 364 | +void tSimpleLivingString_free(tSimpleLivingString* const pl) | |
| 365 | +{ | |
| 366 | + tSimpleLivingString_freeFromPool(pl, &leaf.mempool); | |
| 367 | +} | |
| 368 | + | |
| 369 | +void tSimpleLivingString_initToPool (tSimpleLivingString* const pl, float freq, float dampFreq, | |
| 370 | + float decay, float targetLev, float levSmoothFactor, | |
| 371 | + float levStrength, int levMode, tMempool* const mp) | |
| 372 | +{ | |
| 373 | + _tMempool* m = *mp; | |
| 374 | + _tSimpleLivingString* p = *pl = (_tSimpleLivingString*) mpool_alloc(sizeof(_tSimpleLivingString), m); | |
| 375 | + | |
| 376 | + p->curr=0.0f; | |
| 377 | + tExpSmooth_initToPool(&p->wlSmooth, leaf.sampleRate/freq, 0.01, mp); // smoother for string wavelength (not freq, to avoid expensive divisions) | |
| 378 | + tSimpleLivingString_setFreq(pl, freq); | |
| 379 | + tLinearDelay_initToPool(&p->delayLine,p->waveLengthInSamples, 2400, mp); | |
| 380 | + tLinearDelay_clear(&p->delayLine); | |
| 381 | + tOnePole_initToPool(&p->bridgeFilter, dampFreq, mp); | |
| 382 | + tHighpass_initToPool(&p->DCblocker,13, mp); | |
| 383 | + p->decay=decay; | |
| 384 | + tFeedbackLeveler_initToPool(&p->fbLev, targetLev, levSmoothFactor, levStrength, levMode, mp); | |
| 385 | + p->levMode=levMode; | |
| 386 | +} | |
| 387 | + | |
| 388 | +void tSimpleLivingString_freeFromPool (tSimpleLivingString* const pl, tMempool* const mp) | |
| 389 | +{ | |
| 390 | + _tMempool* m = *mp; | |
| 391 | + _tSimpleLivingString* p = *pl; | |
| 392 | + | |
| 393 | + tExpSmooth_freeFromPool(&p->wlSmooth, mp); | |
| 394 | + tLinearDelay_freeFromPool(&p->delayLine, mp); | |
| 395 | + tOnePole_freeFromPool(&p->bridgeFilter, mp); | |
| 396 | + tHighpass_freeFromPool(&p->DCblocker, mp); | |
| 397 | + tFeedbackLeveler_freeFromPool(&p->fbLev, mp); | |
| 398 | + | |
| 399 | + mpool_free((char*)p, m); | |
| 400 | +} | |
| 401 | + | |
| 402 | +void tSimpleLivingString_setFreq(tSimpleLivingString* const pl, float freq) | |
| 403 | +{ | |
| 404 | + _tSimpleLivingString* p = *pl; | |
| 405 | + | |
| 406 | + if (freq<20) freq=20; | |
| 407 | + else if (freq>10000) freq=10000; | |
| 408 | + p->waveLengthInSamples = leaf.sampleRate/freq; | |
| 409 | + tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples); | |
| 410 | +} | |
| 411 | + | |
| 412 | +void tSimpleLivingString_setWaveLength(tSimpleLivingString* const pl, float waveLength) | |
| 413 | +{ | |
| 414 | + _tSimpleLivingString* p = *pl; | |
| 415 | + | |
| 416 | + if (waveLength<4.8) waveLength=4.8; | |
| 417 | + else if (waveLength>2400) waveLength=2400; | |
| 418 | + p->waveLengthInSamples = waveLength; | |
| 419 | + tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples); | |
| 420 | +} | |
| 421 | + | |
| 422 | +void tSimpleLivingString_setDampFreq(tSimpleLivingString* const pl, float dampFreq) | |
| 423 | +{ | |
| 424 | + _tSimpleLivingString* p = *pl; | |
| 425 | + tOnePole_setFreq(&p->bridgeFilter, dampFreq); | |
| 426 | +} | |
| 427 | + | |
| 428 | +void tSimpleLivingString_setDecay(tSimpleLivingString* const pl, float decay) | |
| 429 | +{ | |
| 430 | + _tSimpleLivingString* p = *pl; | |
| 431 | + p->decay=decay; | |
| 432 | +} | |
| 433 | + | |
| 434 | +void tSimpleLivingString_setTargetLev(tSimpleLivingString* const pl, float targetLev) | |
| 435 | +{ | |
| 436 | + _tSimpleLivingString* p = *pl; | |
| 437 | + tFeedbackLeveler_setTargetLevel(&p->fbLev, targetLev); | |
| 438 | +} | |
| 439 | + | |
| 440 | +void tSimpleLivingString_setLevSmoothFactor(tSimpleLivingString* const pl, float levSmoothFactor) | |
| 441 | +{ | |
| 442 | + _tSimpleLivingString* p = *pl; | |
| 443 | + tFeedbackLeveler_setFactor(&p->fbLev, levSmoothFactor); | |
| 444 | +} | |
| 445 | + | |
| 446 | +void tSimpleLivingString_setLevStrength(tSimpleLivingString* const pl, float levStrength) | |
| 447 | +{ | |
| 448 | + _tSimpleLivingString* p = *pl; | |
| 449 | + tFeedbackLeveler_setStrength(&p->fbLev, levStrength); | |
| 450 | +} | |
| 451 | + | |
| 452 | +void tSimpleLivingString_setLevMode(tSimpleLivingString* const pl, int levMode) | |
| 453 | +{ | |
| 454 | + _tSimpleLivingString* p = *pl; | |
| 455 | + tFeedbackLeveler_setMode(&p->fbLev, levMode); | |
| 456 | + p->levMode=levMode; | |
| 457 | +} | |
| 458 | + | |
| 459 | +float tSimpleLivingString_tick(tSimpleLivingString* const pl, float input) | |
| 460 | +{ | |
| 461 | + _tSimpleLivingString* p = *pl; | |
| 462 | + | |
| 463 | + float stringOut=tOnePole_tick(&p->bridgeFilter,tLinearDelay_tickOut(&p->delayLine)); | |
| 464 | + float stringInput=tHighpass_tick(&p->DCblocker, tFeedbackLeveler_tick(&p->fbLev, (p->levMode==0?p->decay*stringOut:stringOut)+input)); | |
| 465 | + tLinearDelay_tickIn(&p->delayLine, stringInput); | |
| 466 | + tLinearDelay_setDelay(&p->delayLine, tExpSmooth_tick(&p->wlSmooth)); | |
| 467 | + p->curr = stringOut; | |
| 468 | + return p->curr; | |
| 469 | +} | |
| 470 | + | |
| 471 | +float tSimpleLivingString_sample(tSimpleLivingString* const pl) | |
| 472 | +{ | |
| 473 | + _tSimpleLivingString* p = *pl; | |
| 474 | + return p->curr; | |
| 475 | +} | |
| 476 | + | |
| 477 | +/* Living String*/ | |
| 478 | + | |
| 479 | +void tLivingString_init(tLivingString* const pl, float freq, float pickPos, float prepIndex, | |
| 480 | + float dampFreq, float decay, float targetLev, float levSmoothFactor, | |
| 481 | + float levStrength, int levMode) | |
| 482 | +{ | |
| 483 | + tLivingString_initToPool(pl, freq, pickPos, prepIndex, dampFreq, decay, targetLev, levSmoothFactor, levStrength, levMode, &leaf.mempool); | |
| 484 | +} | |
| 485 | + | |
| 486 | +void tLivingString_free(tLivingString* const pl) | |
| 487 | +{ | |
| 488 | + tLivingString_freeFromPool(pl, &leaf.mempool); | |
| 489 | +} | |
| 490 | + | |
| 491 | +void tLivingString_initToPool (tLivingString* const pl, float freq, float pickPos, float prepIndex, | |
| 492 | + float dampFreq, float decay, float targetLev, float levSmoothFactor, | |
| 493 | + float levStrength, int levMode, tMempool* const mp) | |
| 494 | +{ | |
| 495 | + _tMempool* m = *mp; | |
| 496 | + _tLivingString* p = *pl = (_tLivingString*) mpool_alloc(sizeof(_tLivingString), m); | |
| 497 | + | |
| 498 | + p->curr=0.0f; | |
| 499 | + tExpSmooth_initToPool(&p->wlSmooth, leaf.sampleRate/freq, 0.01, mp); // smoother for string wavelength (not freq, to avoid expensive divisions) | |
| 500 | + tLivingString_setFreq(pl, freq); | |
| 501 | + p->freq = freq; | |
| 502 | + tExpSmooth_initToPool(&p->ppSmooth, pickPos, 0.01, mp); // smoother for pick position | |
| 503 | + tLivingString_setPickPos(pl, pickPos); | |
| 504 | + p->prepIndex=prepIndex; | |
| 505 | + tLinearDelay_initToPool(&p->delLF,p->waveLengthInSamples, 2400, mp); | |
| 506 | + tLinearDelay_initToPool(&p->delUF,p->waveLengthInSamples, 2400, mp); | |
| 507 | + tLinearDelay_initToPool(&p->delUB,p->waveLengthInSamples, 2400, mp); | |
| 508 | + tLinearDelay_initToPool(&p->delLB,p->waveLengthInSamples, 2400, mp); | |
| 509 | + tLinearDelay_clear(&p->delLF); | |
| 510 | + tLinearDelay_clear(&p->delUF); | |
| 511 | + tLinearDelay_clear(&p->delUB); | |
| 512 | + tLinearDelay_clear(&p->delLB); | |
| 513 | + p->dampFreq = dampFreq; | |
| 514 | + tOnePole_initToPool(&p->bridgeFilter, dampFreq, mp); | |
| 515 | + tOnePole_initToPool(&p->nutFilter, dampFreq, mp); | |
| 516 | + tOnePole_initToPool(&p->prepFilterU, dampFreq, mp); | |
| 517 | + tOnePole_initToPool(&p->prepFilterL, dampFreq, mp); | |
| 518 | + tHighpass_initToPool(&p->DCblockerU,13, mp); | |
| 519 | + tHighpass_initToPool(&p->DCblockerL,13, mp); | |
| 520 | + p->decay=decay; | |
| 521 | + p->prepIndex = prepIndex; | |
| 522 | + tFeedbackLeveler_initToPool(&p->fbLevU, targetLev, levSmoothFactor, levStrength, levMode, mp); | |
| 523 | + tFeedbackLeveler_initToPool(&p->fbLevL, targetLev, levSmoothFactor, levStrength, levMode, mp); | |
| 524 | + p->levMode=levMode; | |
| 525 | +} | |
| 526 | + | |
| 527 | +void tLivingString_freeFromPool (tLivingString* const pl, tMempool* const mp) | |
| 528 | +{ | |
| 529 | + _tMempool* m = *mp; | |
| 530 | + _tLivingString* p = *pl; | |
| 531 | + | |
| 532 | + tExpSmooth_freeFromPool(&p->wlSmooth, mp); | |
| 533 | + tExpSmooth_freeFromPool(&p->ppSmooth, mp); | |
| 534 | + tLinearDelay_freeFromPool(&p->delLF, mp); | |
| 535 | + tLinearDelay_freeFromPool(&p->delUF, mp); | |
| 536 | + tLinearDelay_freeFromPool(&p->delUB, mp); | |
| 537 | + tLinearDelay_freeFromPool(&p->delLB, mp); | |
| 538 | + tOnePole_freeFromPool(&p->bridgeFilter, mp); | |
| 539 | + tOnePole_freeFromPool(&p->nutFilter, mp); | |
| 540 | + tOnePole_freeFromPool(&p->prepFilterU, mp); | |
| 541 | + tOnePole_freeFromPool(&p->prepFilterL, mp); | |
| 542 | + tHighpass_freeFromPool(&p->DCblockerU, mp); | |
| 543 | + tHighpass_freeFromPool(&p->DCblockerL, mp); | |
| 544 | + tFeedbackLeveler_freeFromPool(&p->fbLevU, mp); | |
| 545 | + tFeedbackLeveler_freeFromPool(&p->fbLevL, mp); | |
| 546 | + | |
| 547 | + mpool_free((char*)p, m); | |
| 548 | +} | |
| 549 | + | |
| 550 | +void tLivingString_setFreq(tLivingString* const pl, float freq) | |
| 551 | +{ // NOTE: It is faster to set wavelength in samples directly | |
| 552 | + _tLivingString* p = *pl; | |
| 553 | + if (freq<20) freq=20; | |
| 554 | + else if (freq>10000) freq=10000; | |
| 555 | + p->waveLengthInSamples = leaf.sampleRate/freq; | |
| 556 | + tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples); | |
| 557 | +} | |
| 558 | + | |
| 559 | +void tLivingString_setWaveLength(tLivingString* const pl, float waveLength) | |
| 560 | +{ | |
| 561 | + _tLivingString* p = *pl; | |
| 562 | + if (waveLength<4.8) waveLength=4.8; | |
| 563 | + else if (waveLength>2400) waveLength=2400; | |
| 564 | + p->waveLengthInSamples = waveLength; | |
| 565 | + tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples); | |
| 566 | +} | |
| 567 | + | |
| 568 | +void tLivingString_setPickPos(tLivingString* const pl, float pickPos) | |
| 569 | +{ // between 0 and 1 | |
| 570 | + _tLivingString* p = *pl; | |
| 571 | + if (pickPos<0.f) pickPos=0.f; | |
| 572 | + else if (pickPos>1.f) pickPos=1.f; | |
| 573 | + p->pickPos = pickPos; | |
| 574 | + tExpSmooth_setDest(&p->ppSmooth, p->pickPos); | |
| 575 | +} | |
| 576 | + | |
| 577 | +void tLivingString_setPrepIndex(tLivingString* const pl, float prepIndex) | |
| 578 | +{ // between 0 and 1 | |
| 579 | + _tLivingString* p = *pl; | |
| 580 | + if (prepIndex<0.f) prepIndex=0.f; | |
| 581 | + else if (prepIndex>1.f) prepIndex=1.f; | |
| 582 | + p->prepIndex = prepIndex; | |
| 583 | +} | |
| 584 | + | |
| 585 | +void tLivingString_setDampFreq(tLivingString* const pl, float dampFreq) | |
| 586 | +{ | |
| 587 | + _tLivingString* p = *pl; | |
| 588 | + tOnePole_setFreq(&p->bridgeFilter, dampFreq); | |
| 589 | + tOnePole_setFreq(&p->nutFilter, dampFreq); | |
| 590 | + tOnePole_setFreq(&p->prepFilterU, dampFreq); | |
| 591 | + tOnePole_setFreq(&p->prepFilterL, dampFreq); | |
| 592 | +} | |
| 593 | + | |
| 594 | +void tLivingString_setDecay(tLivingString* const pl, float decay) | |
| 595 | +{ | |
| 596 | + _tLivingString* p = *pl; | |
| 597 | + p->decay=decay; | |
| 598 | +} | |
| 599 | + | |
| 600 | +void tLivingString_setTargetLev(tLivingString* const pl, float targetLev) | |
| 601 | +{ | |
| 602 | + _tLivingString* p = *pl; | |
| 603 | + tFeedbackLeveler_setTargetLevel(&p->fbLevU, targetLev); | |
| 604 | + tFeedbackLeveler_setTargetLevel(&p->fbLevL, targetLev); | |
| 605 | +} | |
| 606 | + | |
| 607 | +void tLivingString_setLevSmoothFactor(tLivingString* const pl, float levSmoothFactor) | |
| 608 | +{ | |
| 609 | + _tLivingString* p = *pl; | |
| 610 | + tFeedbackLeveler_setFactor(&p->fbLevU, levSmoothFactor); | |
| 611 | + tFeedbackLeveler_setFactor(&p->fbLevL, levSmoothFactor); | |
| 612 | +} | |
| 613 | + | |
| 614 | +void tLivingString_setLevStrength(tLivingString* const pl, float levStrength) | |
| 615 | +{ | |
| 616 | + _tLivingString* p = *pl; | |
| 617 | + tFeedbackLeveler_setStrength(&p->fbLevU, levStrength); | |
| 618 | + tFeedbackLeveler_setStrength(&p->fbLevL, levStrength); | |
| 619 | +} | |
| 620 | + | |
| 621 | +void tLivingString_setLevMode(tLivingString* const pl, int levMode) | |
| 622 | +{ | |
| 623 | + _tLivingString* p = *pl; | |
| 624 | + tFeedbackLeveler_setMode(&p->fbLevU, levMode); | |
| 625 | + tFeedbackLeveler_setMode(&p->fbLevL, levMode); | |
| 626 | + p->levMode=levMode; | |
| 627 | +} | |
| 628 | + | |
| 629 | +float tLivingString_tick(tLivingString* const pl, float input) | |
| 630 | +{ | |
| 631 | + _tLivingString* p = *pl; | |
| 632 | + | |
| 633 | + // from pickPos upwards=forwards | |
| 634 | + float fromLF=tLinearDelay_tickOut(&p->delLF); | |
| 635 | + float fromUF=tLinearDelay_tickOut(&p->delUF); | |
| 636 | + float fromUB=tLinearDelay_tickOut(&p->delUB); | |
| 637 | + float fromLB=tLinearDelay_tickOut(&p->delLB); | |
| 638 | + // into upper half of string, from nut, going backwards | |
| 639 | + float fromNut=-tFeedbackLeveler_tick(&p->fbLevU, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerU, tOnePole_tick(&p->nutFilter, fromUF))); | |
| 640 | + tLinearDelay_tickIn(&p->delUB, fromNut); | |
| 641 | + // into lower half of string, from pickpoint, going backwards | |
| 642 | + float fromLowerPrep=-tOnePole_tick(&p->prepFilterL, fromLF); | |
| 643 | + float intoLower=p->prepIndex*fromLowerPrep+(1.0f - p->prepIndex)*fromUB+input; | |
| 644 | + tLinearDelay_tickIn(&p->delLB, intoLower); | |
| 645 | + // into lower half of string, from bridge | |
| 646 | + float fromBridge=-tFeedbackLeveler_tick(&p->fbLevL, (p->levMode==0?p->decay:1.0f)*tHighpass_tick(&p->DCblockerL, tOnePole_tick(&p->bridgeFilter, fromLB))); | |
| 647 | + tLinearDelay_tickIn(&p->delLF, fromBridge); | |
| 648 | + // into upper half of string, from pickpoint, going forwards/upwards | |
| 649 | + float fromUpperPrep=-tOnePole_tick(&p->prepFilterU, fromUB); | |
| 650 | + float intoUpper=p->prepIndex*fromUpperPrep+(1.0f - p->prepIndex)*fromLF+input; | |
| 651 | + tLinearDelay_tickIn(&p->delUF, intoUpper); | |
| 652 | + // update all delay lengths | |
| 653 | + float pickP=tExpSmooth_tick(&p->ppSmooth); | |
| 654 | + float wLen=tExpSmooth_tick(&p->wlSmooth); | |
| 655 | + float lowLen=pickP*wLen; | |
| 656 | + float upLen=(1.0f-pickP)*wLen; | |
| 657 | + tLinearDelay_setDelay(&p->delLF, lowLen); | |
| 658 | + tLinearDelay_setDelay(&p->delLB, lowLen); | |
| 659 | + tLinearDelay_setDelay(&p->delUF, upLen); | |
| 660 | + tLinearDelay_setDelay(&p->delUB, upLen); | |
| 661 | + p->curr = fromBridge; | |
| 662 | + return p->curr; | |
| 663 | +} | |
| 664 | + | |
| 665 | +float tLivingString_sample(tLivingString* const pl) | |
| 666 | +{ | |
| 667 | + _tLivingString* p = *pl; | |
| 668 | + return p->curr; | |
| 669 | +} | |
| 670 | + | |
| 671 | +///Reed Table model | |
| 672 | +//default values from STK are 0.6 offset and -0.8 slope | |
| 673 | + | |
| 674 | +void tReedTable_init (tReedTable* const pm, float offset, float slope) | |
| 675 | +{ | |
| 676 | + _tReedTable* p = *pm = (_tReedTable*) leaf_alloc(sizeof(_tReedTable)); | |
| 677 | + | |
| 678 | + p->offset = offset; | |
| 679 | + p->slope = slope; | |
| 680 | +} | |
| 681 | + | |
| 682 | +void tReedTable_free (tReedTable* const pm) | |
| 683 | +{ | |
| 684 | + _tReedTable* p = *pm; | |
| 685 | + | |
| 686 | + leaf_free((char*)p); | |
| 687 | +} | |
| 688 | + | |
| 689 | +void tReedTable_initToPool (tReedTable* const pm, float offset, float slope, tMempool* const mp) | |
| 690 | +{ | |
| 691 | + _tMempool* m = *mp; | |
| 692 | + _tReedTable* p = *pm = (_tReedTable*) mpool_alloc(sizeof(_tReedTable), m); | |
| 693 | + | |
| 694 | + p->offset = offset; | |
| 695 | + p->slope = slope; | |
| 696 | +} | |
| 697 | + | |
| 698 | +void tReedTable_freeFromPool (tReedTable* const pm, tMempool* const mp) | |
| 699 | +{ | |
| 700 | + _tMempool* m = *mp; | |
| 701 | + _tReedTable* p = *pm; | |
| 702 | + | |
| 703 | + mpool_free((char*)p, m); | |
| 704 | +} | |
| 705 | + | |
| 706 | +float tReedTable_tick (tReedTable* const pm, float input) | |
| 707 | +{ | |
| 708 | + _tReedTable* p = *pm; | |
| 709 | + | |
| 710 | + // The input is differential pressure across the reed. | |
| 711 | + float output = p->offset + (p->slope * input); | |
| 712 | + | |
| 713 | + // If output is > 1, the reed has slammed shut and the | |
| 714 | + // reflection function value saturates at 1.0. | |
| 715 | + if ( output > 1.0f) output = 1.0f; | |
| 716 | + | |
| 717 | + // This is nearly impossible in a physical system, but | |
| 718 | + // a reflection function value of -1.0 corresponds to | |
| 719 | + // an open end (and no discontinuity in bore profile). | |
| 720 | + if ( output < -1.0f) output = -1.0f; | |
| 721 | + | |
| 722 | + return output; | |
| 723 | +} | |
| 724 | + | |
| 725 | +float tReedTable_tanh_tick (tReedTable* const pm, float input) | |
| 726 | +{ | |
| 727 | + _tReedTable* p = *pm; | |
| 728 | + | |
| 729 | + // The input is differential pressure across the reed. | |
| 730 | + float output = p->offset + (p->slope * input); | |
| 731 | + | |
| 732 | + // If output is > 1, the reed has slammed shut and the | |
| 733 | + // reflection function value saturates at 1.0. | |
| 734 | + return tanhf(output); | |
| 735 | +} | |
| 736 | + | |
| 737 | +void tReedTable_setOffset (tReedTable* const pm, float offset) | |
| 738 | +{ | |
| 739 | + _tReedTable* p = *pm; | |
| 740 | + p->offset = offset; | |
| 741 | +} | |
| 742 | + | |
| 743 | +void tReedTable_setSlope (tReedTable* const pm, float slope) | |
| 744 | +{ | |
| 745 | + _tReedTable* p = *pm; | |
| 746 | + p->slope = slope; | |
| 747 | +} | |
| @@ -0,0 +1,925 @@ | ||
| 1 | +/*============================================================================== | |
| 2 | + | |
| 3 | + leaf-reverb.c | |
| 4 | + Created: 20 Jan 2017 12:02:04pm | |
| 5 | + Author: Michael R Mulshine | |
| 6 | + | |
| 7 | +==============================================================================*/ | |
| 8 | + | |
| 9 | +#if _WIN32 || _WIN64 | |
| 10 | + | |
| 11 | +#include "..\Inc\leaf-reverb.h" | |
| 12 | +#include "..\leaf.h" | |
| 13 | + | |
| 14 | +#else | |
| 15 | + | |
| 16 | +#include "../Inc/leaf-reverb.h" | |
| 17 | +#include "../leaf.h" | |
| 18 | + | |
| 19 | +#endif | |
| 20 | + | |
| 21 | +// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ PRCReverb ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // | |
| 22 | +void tPRCReverb_init(tPRCReverb* const rev, float t60) | |
| 23 | +{ | |
| 24 | + _tPRCReverb* r = *rev = (_tPRCReverb*) leaf_alloc(sizeof(_tPRCReverb)); | |
| 25 | + | |
| 26 | + if (t60 <= 0.0f) t60 = 0.001f; | |
| 27 | + | |
| 28 | + r->inv_441 = 1.0f/44100.0f; | |
| 29 | + | |
| 30 | + int lengths[4] = { 341, 613, 1557, 2137 }; // Delay lengths for 44100 Hz sample rate. | |
| 31 | + double scaler = leaf.sampleRate * r->inv_441; | |
| 32 | + | |
| 33 | + int delay, i; | |
| 34 | + if (scaler != 1.0f) | |
| 35 | + { | |
| 36 | + for (i=0; i<4; i++) | |
| 37 | + { | |
| 38 | + delay = (int) scaler * lengths[i]; | |
| 39 | + | |
| 40 | + if ( (delay & 1) == 0) delay++; | |
| 41 | + | |
| 42 | + while ( !LEAF_isPrime(delay) ) delay += 2; | |
| 43 | + | |
| 44 | + lengths[i] = delay; | |
| 45 | + } | |
| 46 | + } | |
| 47 | + | |
| 48 | + tDelay_init(&r->allpassDelays[0], lengths[0], lengths[0] * 2); | |
| 49 | + tDelay_init(&r->allpassDelays[1], lengths[1], lengths[1] * 2); | |
| 50 | + tDelay_init(&r->combDelay, lengths[2], lengths[2] * 2); | |
| 51 | + | |
| 52 | + tPRCReverb_setT60(rev, t60); | |
| 53 | + | |
| 54 | + r->allpassCoeff = 0.7f; | |
| 55 | + r->mix = 0.5f; | |
| 56 | +} | |
| 57 | + | |
| 58 | +void tPRCReverb_free(tPRCReverb* const rev) | |
| 59 | +{ | |
| 60 | + _tPRCReverb* r = *rev; | |
| 61 | + | |
| 62 | + tDelay_free(&r->allpassDelays[0]); | |
| 63 | + tDelay_free(&r->allpassDelays[1]); | |
| 64 | + tDelay_free(&r->combDelay); | |
| 65 | + leaf_free((char*)r); | |
| 66 | +} | |
| 67 | + | |
| 68 | +void tPRCReverb_initToPool (tPRCReverb* const rev, float t60, tMempool* const mp) | |
| 69 | +{ | |
| 70 | + _tMempool* m = *mp; | |
| 71 | + _tPRCReverb* r = *rev = (_tPRCReverb*) mpool_alloc(sizeof(_tPRCReverb), m); | |
| 72 | + | |
| 73 | + if (t60 <= 0.0f) t60 = 0.001f; | |
| 74 | + | |
| 75 | + r->inv_441 = 1.0f/44100.0f; | |
| 76 | + | |
| 77 | + int lengths[4] = { 341, 613, 1557, 2137 }; // Delay lengths for 44100 Hz sample rate. | |
| 78 | + double scaler = leaf.sampleRate * r->inv_441; | |
| 79 | + | |
| 80 | + int delay, i; | |
| 81 | + if (scaler != 1.0f) | |
| 82 | + { | |
| 83 | + for (i=0; i<4; i++) | |
| 84 | + { | |
| 85 | + delay = (int) scaler * lengths[i]; | |
| 86 | + | |
| 87 | + if ( (delay & 1) == 0) delay++; | |
| 88 | + | |
| 89 | + while ( !LEAF_isPrime(delay) ) delay += 2; | |
| 90 | + | |
| 91 | + lengths[i] = delay; | |
| 92 | + } | |
| 93 | + } | |
| 94 | + | |
| 95 | + tDelay_initToPool(&r->allpassDelays[0], lengths[0], lengths[0] * 2, mp); | |
| 96 | + tDelay_initToPool(&r->allpassDelays[1], lengths[1], lengths[1] * 2, mp); | |
| 97 | + tDelay_initToPool(&r->combDelay, lengths[2], lengths[2] * 2, mp); | |
| 98 | + | |
| 99 | + tPRCReverb_setT60(rev, t60); | |
| 100 | + | |
| 101 | + r->allpassCoeff = 0.7f; | |
| 102 | + r->mix = 0.5f; | |
| 103 | +} | |
| 104 | + | |
| 105 | +void tPRCReverb_freeFromPool (tPRCReverb* const rev, tMempool* const mp) | |
| 106 | +{ | |
| 107 | + _tMempool* m = *mp; | |
| 108 | + _tPRCReverb* r = *rev; | |
| 109 | + | |
| 110 | + tDelay_freeFromPool(&r->allpassDelays[0], mp); | |
| 111 | + tDelay_freeFromPool(&r->allpassDelays[1], mp); | |
| 112 | + tDelay_freeFromPool(&r->combDelay, mp); | |
| 113 | + mpool_free((char*)r, m); | |
| 114 | +} | |
| 115 | + | |
| 116 | +void tPRCRevert_clear(tPRCReverb* const rev) | |
| 117 | +{ | |
| 118 | + _tPRCReverb* r = *rev; | |
| 119 | + | |
| 120 | + tDelay_clear(&r->allpassDelays[0]); | |
| 121 | + tDelay_clear(&r->allpassDelays[1]); | |
| 122 | + tDelay_clear(&r->combDelay); | |
| 123 | +} | |
| 124 | + | |
| 125 | +void tPRCReverb_setT60(tPRCReverb* const rev, float t60) | |
| 126 | +{ | |
| 127 | + _tPRCReverb* r = *rev; | |
| 128 | + | |
| 129 | + if ( t60 <= 0.0f ) t60 = 0.001f; | |
| 130 | + | |
| 131 | + r->t60 = t60; | |
| 132 | + | |
| 133 | + r->combCoeff = pow(10.0f, (-3.0f * tDelay_getDelay(&r->combDelay) * leaf.invSampleRate / t60 )); | |
| 134 | + | |
| 135 | +} | |
| 136 | + | |
| 137 | +void tPRCReverb_setMix(tPRCReverb* const rev, float mix) | |
| 138 | +{ | |
| 139 | + _tPRCReverb* r = *rev; | |
| 140 | + r->mix = mix; | |
| 141 | +} | |
| 142 | + | |
| 143 | +float tPRCReverb_tick(tPRCReverb* const rev, float input) | |
| 144 | +{ | |
| 145 | + _tPRCReverb* r = *rev; | |
| 146 | + | |
| 147 | + float temp, temp0, temp1, temp2; | |
| 148 | + float out; | |
| 149 | + | |
| 150 | + r->lastIn = input; | |
| 151 | + | |
| 152 | + temp = tDelay_getLastOut(&r->allpassDelays[0]); | |
| 153 | + temp0 = r->allpassCoeff * temp; | |
| 154 | + temp0 += input; | |
| 155 | + tDelay_tick(&r->allpassDelays[0], temp0); | |
| 156 | + temp0 = -( r->allpassCoeff * temp0) + temp; | |
| 157 | + | |
| 158 | + temp = tDelay_getLastOut(&r->allpassDelays[1]); | |
| 159 | + temp1 = r->allpassCoeff * temp; | |
| 160 | + temp1 += temp0; | |
| 161 | + tDelay_tick(&r->allpassDelays[1], temp1); | |
| 162 | + temp1 = -(r->allpassCoeff * temp1) + temp; | |
| 163 | + | |
| 164 | + temp2 = temp1 + ( r->combCoeff * tDelay_getLastOut(&r->combDelay)); | |
| 165 | + | |
| 166 | + out = r->mix * tDelay_tick(&r->combDelay, temp2); | |
| 167 | + | |
| 168 | + temp = (1.0f - r->mix) * input; | |
| 169 | + | |
| 170 | + out += temp; | |
| 171 | + | |
| 172 | + r->lastOut = out; | |
| 173 | + | |
| 174 | + return out; | |
| 175 | +} | |
| 176 | + | |
| 177 | +void tPRCReverbSampleRateChanged (tPRCReverb* const rev) | |
| 178 | +{ | |
| 179 | + _tPRCReverb* r = *rev; | |
| 180 | + r->combCoeff = pow(10.0f, (-3.0f * tDelay_getDelay(&r->combDelay) * leaf.invSampleRate / r->t60 )); | |
| 181 | +} | |
| 182 | + | |
| 183 | +/* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ NReverb ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ */ | |
| 184 | +void tNReverb_init(tNReverb* const rev, float t60) | |
| 185 | +{ | |
| 186 | + _tNReverb* r = *rev = (_tNReverb*) leaf_alloc(sizeof(_tNReverb)); | |
| 187 | + | |
| 188 | + if (t60 <= 0.0f) t60 = 0.001f; | |
| 189 | + | |
| 190 | + r->inv_441 = 1.0f/44100.0f; | |
| 191 | + | |
| 192 | + int lengths[15] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 53, 43, 37, 29, 19}; // Delay lengths for 44100 Hz sample rate. | |
| 193 | + double scaler = leaf.sampleRate / 25641.0f; | |
| 194 | + | |
| 195 | + int delay, i; | |
| 196 | + | |
| 197 | + for (i=0; i < 15; i++) | |
| 198 | + { | |
| 199 | + delay = (int) scaler * lengths[i]; | |
| 200 | + if ( (delay & 1) == 0) | |
| 201 | + delay++; | |
| 202 | + while ( !LEAF_isPrime(delay) ) | |
| 203 | + delay += 2; | |
| 204 | + lengths[i] = delay; | |
| 205 | + } | |
| 206 | + | |
| 207 | + for ( i=0; i<6; i++ ) | |
| 208 | + { | |
| 209 | + tLinearDelay_init(&r->combDelays[i], lengths[i], lengths[i] * 2.0f); | |
| 210 | + tLinearDelay_clear(&r->combDelays[i]); | |
| 211 | + r->combCoeffs[i] = pow(10.0, (-3 * lengths[i] * leaf.invSampleRate / t60)); | |
| 212 | + } | |
| 213 | + | |
| 214 | + for ( i=0; i<8; i++ ) | |
| 215 | + { | |
| 216 | + tLinearDelay_init(&r->allpassDelays[i], lengths[i+6], lengths[i+6] * 2.0f); | |
| 217 | + tLinearDelay_clear(&r->allpassDelays[i]); | |
| 218 | + } | |
| 219 | + | |
| 220 | + | |
| 221 | + tNReverb_setT60(rev, t60); | |
| 222 | + r->allpassCoeff = 0.7f; | |
| 223 | + r->mix = 0.3f; | |
| 224 | +} | |
| 225 | + | |
| 226 | +void tNReverb_free(tNReverb* const rev) | |
| 227 | +{ | |
| 228 | + _tNReverb* r = *rev; | |
| 229 | + | |
| 230 | + for (int i = 0; i < 6; i++) | |
| 231 | + { | |
| 232 | + tLinearDelay_free(&r->combDelays[i]); | |
| 233 | + } | |
| 234 | + | |
| 235 | + for (int i = 0; i < 8; i++) | |
| 236 | + { | |
| 237 | + tLinearDelay_free(&r->allpassDelays[i]); | |
| 238 | + } | |
| 239 | + | |
| 240 | + leaf_free((char*)r); | |
| 241 | +} | |
| 242 | + | |
| 243 | +void tNReverb_initToPool (tNReverb* const rev, float t60, tMempool* const mp) | |
| 244 | +{ | |
| 245 | + _tMempool* m = *mp; | |
| 246 | + _tNReverb* r = *rev = (_tNReverb*) mpool_alloc(sizeof(_tNReverb), m); | |
| 247 | + | |
| 248 | + if (t60 <= 0.0f) t60 = 0.001f; | |
| 249 | + | |
| 250 | + r->inv_441 = 1.0f/44100.0f; | |
| 251 | + | |
| 252 | + int lengths[15] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 53, 43, 37, 29, 19}; // Delay lengths for 44100 Hz sample rate. | |
673 more lines aren’t shown here.
This diff is large: its 867 lines aren’t shown here.
This diff is large: its 14062 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 155 lines aren’t shown here.