File: GendynUGens.cpp

package info (click to toggle)
supercollider 1%3A3.13.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 80,292 kB
  • sloc: cpp: 476,363; lisp: 84,680; ansic: 77,685; sh: 25,509; python: 7,909; makefile: 3,440; perl: 1,964; javascript: 974; xml: 826; java: 677; yacc: 314; lex: 175; objc: 152; ruby: 136
file content (650 lines) | stat: -rw-r--r-- 21,508 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
/*
    SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
    http://www.audiosynth.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

// Gendyn UGens implemented by Nick Collins

#include "SC_PlugIn.h"

static InterfaceTable* ft;

struct Gendy1 : public Unit // Iannis Xenakis/Marie-Helene Serra GENDYN simulation
{
    double mPhase;
    float mFreqMul, mAmp, mNextAmp, mSpeed, mDur;
    int mMemorySize, mIndex;
    float* mMemoryAmp; // could hard code as 12
    float* mMemoryDur;
};

// following Hoffmann paper from CMJ- primary and secondary random walks
struct Gendy2 : public Unit {
    double mPhase;
    float mFreqMul, mAmp, mNextAmp, mSpeed, mDur;
    int mMemorySize, mIndex;
    float* mMemoryAmp;
    float* mMemoryAmpStep;
    float* mMemoryDur;
    float* mMemoryDurStep;
};


// Random walks as Gendy1 but works out all breakpoints per cycle and normalises time intervals to desired frequency
struct Gendy3 : public Unit {
    double mPhase, mNextPhase, mLastPhase;
    float mSpeed, mFreqMul;
    float mAmp, mNextAmp, mInterpMult;
    int mMemorySize, mIndex;
    float* mMemoryAmp;
    float* mMemoryDur;
    double* mPhaseList;
    float* mAmpList;
};

extern "C" {

void Gendy1_next_k(Gendy1* unit, int inNumSamples);
void Gendy1_Ctor(Gendy1* unit);
void Gendy1_Dtor(Gendy1* unit);

void Gendy2_next_k(Gendy2* unit, int inNumSamples);
void Gendy2_Ctor(Gendy2* unit);
void Gendy2_Dtor(Gendy2* unit);

void Gendy3_next_k(Gendy3* unit, int inNumSamples);
void Gendy3_Ctor(Gendy3* unit);
void Gendy3_Dtor(Gendy3* unit);
}


void Gendy1_Ctor(Gendy1* unit) {
    SETCALC(Gendy1_next_k);

    unit->mFreqMul = unit->mRate->mSampleDur;
    unit->mPhase = 1.0; // should immediately decide on new target
    unit->mAmp = 0.0f;
    unit->mNextAmp = 0.0f;
    unit->mSpeed = 100.f;

    unit->mMemorySize = (int)ZIN0(8); // default is 12
    // printf("memsize %d %f", unit->mMemorySize, ZIN0(8));
    if (unit->mMemorySize < 1)
        unit->mMemorySize = 1;
    unit->mIndex = 0;
    unit->mMemoryAmp = (float*)RTAlloc(unit->mWorld, unit->mMemorySize * sizeof(float));
    unit->mMemoryDur = (float*)RTAlloc(unit->mWorld, unit->mMemorySize * sizeof(float));
    ClearUnitIfMemFailed(unit->mMemoryAmp && unit->mMemoryDur);

    RGen& rgen = *unit->mParent->mRGen;

    // initialise to zeroes and separations
    for (int i = 0; i < unit->mMemorySize; ++i) {
        unit->mMemoryAmp[i] = 2 * rgen.frand() - 1.0f;
        unit->mMemoryDur[i] = rgen.frand();
    }

    // compute one sample of output to avoid sending garbage memory downstream to other Ctor functions
    // first sample of the _next output will be the current amplitude (which is 0)
    OUT0(0) = 0.0f;
}

void Gendy1_Dtor(Gendy1* unit) {
    RTFree(unit->mWorld, unit->mMemoryAmp);
    RTFree(unit->mWorld, unit->mMemoryDur);
}


// called once per period so OK to work out constants in here
static float Gendyn_distribution(int which, float a, float f) {
    float temp, c;

    if (a > 1.f)
        a = 1.f; // a must be in range 0 to 1
    if (a < 0.0001f)
        a = 0.0001f; // for safety with some distributions, don't want divide by zero errors

    switch (which) {
    case 0: // LINEAR
            // linear
        break;
    case 1: // CAUCHY
        // X has a*tan((z-0.5)*pi)
        // I went back to first principles of the Cauchy distribution and re-integrated with a
        // normalisation constant

        // choice of 10 here is such that f=0.95 gives about 0.35 for temp, could go with 2 to make it finer
        c = atan(10 * a); // PERHAPS CHANGE TO a=1/a;
        // incorrect- missed out divisor of pi in norm temp= a*tan(c*(2*pi*f - 1));
        temp = (1.f / a) * tan(c * (2.f * f - 1.f)); // Cauchy distribution, C is precalculated

        // printf("cauchy f %f c %f temp %f out %f \n",f,  c, temp, temp/10);

        return temp * 0.1f; //(temp+100)/200;

    case 2: // LOGIST (ic)
        // X has -(log((1-z)/z)+b)/a which is not very usable as is

        c = 0.5f + (0.499f * a); // calculate normalisation constant
        c = log((1.f - c) / c);

        // remap into range of valid inputs to avoid infinities in the log

        // f= ((f-0.5)*0.499*a)+0.5;
        f = ((f - 0.5f) * 0.998f * a) + 0.5f; //[0,1]->[0.001,0.999]; squashed around midpoint 0.5 by a
        // Xenakis calls this the LOGIST map, it's from the range [0,1] to [inf,0] where 0.5->1
        // than take natural log. to avoid infinities in practise I take [0,1] -> [0.001,0.999]->[6.9,-6.9]
        // an interesting property is that 0.5-e is the reciprocal of 0.5+e under (1-f)/f
        // and hence the logs are the negative of each other
        temp = log((1.f - f) / f) / c; // n range [-1,1]
        // X also had two constants in his- I don't bother

        // printf("logist f %f temp %f\n", f, temp);

        return temp; // a*0.5*(temp+1.0);	//to [0,1]

    case 3: // HYPERBCOS
        // X original a*log(tan(z*pi/2)) which is [0,1]->[0,pi/2]->[0,inf]->[-inf,inf]
        // unmanageable in this pure form
        c = tan(1.5692255f * a); // tan(0.999*a*pi*0.5);    	//[0, 636.6] maximum range
        temp = tan(1.5692255f * a * f) / c; //[0,1]->[0,1]
        temp = log(temp * 0.999f + 0.001f) * (-0.1447648f); // multiplier same as /(-6.9077553); //[0,1]->[0,1]

        // printf("hyperbcos f %f c %f temp %f\n", f, c, temp);

        return 2.f * temp - 1.0f;

    case 4: // ARCSINE
        // X original a/2*(1-sin((0.5-z)*pi)) aha almost a better behaved one though [0,1]->[2,0]->[a,0]
        c = sin(1.5707963f * a); // sin(pi*0.5*a);	//a as scaling factor of domain of sine input to use
        temp = sin(pi_f * (f - 0.5f) * a) / c; //[-1,1] which is what I need

        // printf("arcsine f %f c %f temp %f\n", f, c, temp);

        return temp;

    case 5: // EXPON
        // X original -(log(1-z))/a [0,1]-> [1,0]-> [0,-inf]->[0,inf]
        c = log(1.f - (0.999f * a));
        temp = log(1.f - (f * 0.999f * a)) / c;

        // printf("expon f %f c %f temp %f\n", f, c, temp);

        return 2.f * temp - 1.f;

    case 6: // SINUS
        // X original a*sin(smp * 2*pi/44100 * b) ie depends on a second oscillator's value-
        // hmmm, plug this in as a I guess, will automatically accept control rate inputs then!
        return 2.f * a - 1.f;

    default:
        break;
    }

    return 2.f * f - 1.f;
}


void Gendy1_next_k(Gendy1* unit, int inNumSamples) {
    float* out = ZOUT(0);

    // distribution choices for amp and dur and constants of distribution
    int whichamp = (int)ZIN0(0);
    int whichdur = (int)ZIN0(1);
    float aamp = ZIN0(2);
    float adur = ZIN0(3);
    float minfreq = ZIN0(4);
    float maxfreq = ZIN0(5);
    float scaleamp = ZIN0(6);
    float scaledur = ZIN0(7);

    float rate = unit->mDur;

    // phase gives proportion for linear interpolation automatically
    double phase = unit->mPhase;

    float amp = unit->mAmp;
    float nextamp = unit->mNextAmp;

    float speed = unit->mSpeed;

    RGen& rgen = *unit->mParent->mRGen;
    // linear distribution 0.0 to 1.0 using rgen.frand()

    LOOP1(
        inNumSamples, float z;

        if (phase >= 1.0) {
            phase -= 1.0;

            int index = unit->mIndex;
            int num = (int)(ZIN0(9)); //(unit->mMemorySize);(((int)ZIN0(9))%(unit->mMemorySize))+1;
            if ((num > (unit->mMemorySize)) || (num < 1))
                num = unit->mMemorySize;

            // new code for indexing
            index = (index + 1) % num;
            amp = nextamp;

            unit->mIndex = index;

            // Gendy dist gives value [-1,1], then use scaleamp
            // first term was amp before, now must check new memory slot
            nextamp = (unit->mMemoryAmp[index]) + (scaleamp * Gendyn_distribution(whichamp, aamp, rgen.frand()));

            // mirroring for bounds- safe version
            if (nextamp > 1.0f || nextamp < -1.0f) {
                // printf("mirroring nextamp %f ", nextamp);

                // to force mirroring to be sensible
                if (nextamp < 0.0f)
                    nextamp = nextamp + 4.f;

                nextamp = fmod(nextamp, 4.f);
                // printf("fmod  %f ", nextamp);

                if (nextamp > 1.0f && nextamp < 3.f)
                    nextamp = 2.f - nextamp;

                else if (nextamp > 1.0f)
                    nextamp = nextamp - 4.f;

                // printf("mirrorednextamp %f \n", nextamp);
            };

            unit->mMemoryAmp[index] = nextamp;

            // Gendy dist gives value [-1,1]
            rate = (unit->mMemoryDur[index]) + (scaledur * Gendyn_distribution(whichdur, adur, rgen.frand()));

            if (rate > 1.0f || rate < 0.0f) {
                if (rate < 0.0)
                    rate = rate + 2.f;
                rate = fmod(rate, 2.0f);
                rate = 2.f - rate;
            }

            unit->mMemoryDur[index] = rate;

            // printf("nextamp %f rate %f \n", nextamp, rate);

            // define range of speeds (say between 20 and 1000 Hz)
            // can have bounds as fourth and fifth inputs
            speed = (minfreq + ((maxfreq - minfreq) * rate)) * (unit->mFreqMul);

            // if there are 12 control points in memory, that is 12 per cycle
            // the speed is multiplied by 12
            //(I don't store this because updating rates must remain in range [0,1]
            speed *= num;
        }

        // linear interpolation could be changed
        z = ((1.0 - phase) * amp) + (phase * nextamp);

        phase += speed; ZXP(out) = z;);

    unit->mPhase = phase;
    unit->mAmp = amp;
    unit->mNextAmp = nextamp;
    unit->mSpeed = speed;
    unit->mDur = rate;
}


void Gendy2_Ctor(Gendy2* unit) {
    SETCALC(Gendy2_next_k);

    unit->mFreqMul = unit->mRate->mSampleDur;
    unit->mPhase = 1.0; // should immediately decide on new target
    unit->mAmp = 0.0f;
    unit->mNextAmp = 0.0f;
    unit->mSpeed = 100.f;

    unit->mMemorySize = (int)ZIN0(8); // default is 12
    // printf("memsize %d %f", unit->mMemorySize, ZIN0(8));
    if (unit->mMemorySize < 1)
        unit->mMemorySize = 1;
    unit->mIndex = 0;
    unit->mMemoryAmp = (float*)RTAlloc(unit->mWorld, unit->mMemorySize * sizeof(float));
    unit->mMemoryDur = (float*)RTAlloc(unit->mWorld, unit->mMemorySize * sizeof(float));
    unit->mMemoryAmpStep = (float*)RTAlloc(unit->mWorld, unit->mMemorySize * sizeof(float));
    unit->mMemoryDurStep = (float*)RTAlloc(unit->mWorld, unit->mMemorySize * sizeof(float));
    ClearUnitIfMemFailed(unit->mMemoryAmp && unit->mMemoryDur && unit->mMemoryAmpStep && unit->mMemoryDurStep);

    RGen& rgen = *unit->mParent->mRGen;

    // initialise to zeroes and separations
    for (int i = 0; i < unit->mMemorySize; ++i) {
        unit->mMemoryAmp[i] = 2 * rgen.frand() - 1.0f;
        unit->mMemoryDur[i] = rgen.frand();
        unit->mMemoryAmpStep[i] = 2 * rgen.frand() - 1.0f;
        unit->mMemoryDurStep[i] = 2 * rgen.frand() - 1.0f;
    }

    // compute one sample of output to avoid sending garbage memory downstream to other Ctor functions
    // first sample of the _next output will be the current amplitude (which is 0)
    OUT0(0) = 0.0f;
}

void Gendy2_Dtor(Gendy2* unit) {
    RTFree(unit->mWorld, unit->mMemoryAmp);
    RTFree(unit->mWorld, unit->mMemoryDur);
    RTFree(unit->mWorld, unit->mMemoryAmpStep);
    RTFree(unit->mWorld, unit->mMemoryDurStep);
}

static float Gendyn_mirroring(float lower, float upper, float in) {
    // mirroring for bounds- safe version
    if (in > upper || in < lower) {
        float range = (upper - lower);

        if (in < lower)
            in = (2.0f * upper - lower) - in;

        in = fmod(in - upper, 2.0f * range);

        if (in < range)
            in = upper - in;
        else
            in = in - (range);
    }

    return in;
}

void Gendy2_next_k(Gendy2* unit, int inNumSamples) {
    float* out = ZOUT(0);

    // distribution choices for amp and dur and constants of distribution
    int whichamp = (int)ZIN0(0);
    int whichdur = (int)ZIN0(1);
    float aamp = ZIN0(2);
    float adur = ZIN0(3);
    float minfreq = ZIN0(4);
    float maxfreq = ZIN0(5);
    float scaleamp = ZIN0(6);
    float scaledur = ZIN0(7);

    float rate = unit->mDur;

    // phase gives proportion for linear interpolation automatically
    double phase = unit->mPhase;

    float amp = unit->mAmp;
    float nextamp = unit->mNextAmp;

    float speed = unit->mSpeed;

    RGen& rgen = *unit->mParent->mRGen;

    LOOP1(
        inNumSamples, float z;

        if (phase >= 1.0) {
            phase -= 1.0;

            int index = unit->mIndex;
            int num = (int)(ZIN0(9)); //(unit->mMemorySize);(((int)ZIN0(9))%(unit->mMemorySize))+1;
            if ((num > (unit->mMemorySize)) || (num < 1))
                num = unit->mMemorySize;

            // new code for indexing
            index = (index + 1) % num;

            // using last amp value as seed
            // random values made using a lehmer number generator xenakis style
            float a = ZIN0(10);
            float c = ZIN0(11);

            float lehmerxen = fmod(((amp)*a) + c, 1.0f);

            // printf("lehmer %f \n", lehmerxen);

            amp = nextamp;

            unit->mIndex = index;

            // Gendy dist gives value [-1,1], then use scaleamp
            // first term was amp before, now must check new memory slot

            float ampstep = (unit->mMemoryAmpStep[index]) + Gendyn_distribution(whichamp, aamp, fabs(lehmerxen));
            ampstep = Gendyn_mirroring(-1.0f, 1.0f, ampstep);

            unit->mMemoryAmpStep[index] = ampstep;

            nextamp = (unit->mMemoryAmp[index]) + (scaleamp * ampstep);

            nextamp = Gendyn_mirroring(-1.0f, 1.0f, nextamp);

            unit->mMemoryAmp[index] = nextamp;

            float durstep = (unit->mMemoryDurStep[index]) + Gendyn_distribution(whichdur, adur, rgen.frand());
            durstep = Gendyn_mirroring(-1.0f, 1.0f, durstep);

            unit->mMemoryDurStep[index] = durstep;

            rate = (unit->mMemoryDur[index]) + (scaledur * durstep);

            rate = Gendyn_mirroring(0.0f, 1.0f, rate);

            unit->mMemoryDur[index] = rate;

            // printf("nextamp %f rate %f \n", nextamp, rate);

            // define range of speeds (say between 20 and 1000 Hz)
            // can have bounds as fourth and fifth inputs
            speed = (minfreq + ((maxfreq - minfreq) * rate)) * (unit->mFreqMul);

            // if there are 12 control points in memory, that is 12 per cycle
            // the speed is multiplied by 12
            //(I don't store this because updating rates must remain in range [0,1]
            speed *= num;
        }

        // linear interpolation could be changed
        z = ((1.0 - phase) * amp) + (phase * nextamp);

        phase += speed; ZXP(out) = z;);

    unit->mPhase = phase;
    unit->mAmp = amp;
    unit->mNextAmp = nextamp;
    unit->mSpeed = speed;
    unit->mDur = rate;
}

void Gendy3_Ctor(Gendy3* unit) {
    SETCALC(Gendy3_next_k);

    unit->mFreqMul = unit->mRate->mSampleDur;
    unit->mPhase = 1.0; // should immediately decide on new target
    unit->mAmp = 0.0f;
    unit->mNextAmp = 0.0f;
    unit->mNextPhase = 0.0;
    unit->mLastPhase = 0.0;
    unit->mInterpMult = 1.0f;
    unit->mSpeed = 100.f;

    unit->mMemorySize = (int)ZIN0(7);
    if (unit->mMemorySize < 1)
        unit->mMemorySize = 1;
    unit->mIndex = 0;
    unit->mMemoryAmp = (float*)RTAlloc(unit->mWorld, unit->mMemorySize * sizeof(float));
    unit->mMemoryDur = (float*)RTAlloc(unit->mWorld, unit->mMemorySize * sizeof(float));

    // one more in amp list for guard (wrap) element
    unit->mAmpList = (float*)RTAlloc(unit->mWorld, (unit->mMemorySize + 1) * sizeof(float));
    unit->mPhaseList = (double*)RTAlloc(unit->mWorld, (unit->mMemorySize + 1) * sizeof(double));
    ClearUnitIfMemFailed(unit->mMemoryAmp && unit->mMemoryDur && unit->mAmpList && unit->mPhaseList);

    RGen& rgen = *unit->mParent->mRGen;

    // initialise to zeroes and separations
    for (int i = 0; i < unit->mMemorySize; ++i) {
        unit->mMemoryAmp[i] = 2 * rgen.frand() - 1.0f;
        unit->mMemoryDur[i] = rgen.frand();
        unit->mAmpList[i] = 2 * rgen.frand() - 1.0f;
        unit->mPhaseList[i] = 1.0; // will be intialised immediately
    }

    unit->mMemoryAmp[0] = 0.0f; // always zeroed first BP

    // compute one sample of output to avoid sending garbage memory downstream to other Ctor functions
    // first sample of the _next output will be the current amplitude (which is 0)
    OUT0(0) = 0.0f;
}

void Gendy3_Dtor(Gendy3* unit) {
    RTFree(unit->mWorld, unit->mMemoryAmp);
    RTFree(unit->mWorld, unit->mMemoryDur);
    RTFree(unit->mWorld, unit->mAmpList);
    RTFree(unit->mWorld, unit->mPhaseList);
}

void Gendy3_next_k(Gendy3* unit, int inNumSamples) {
    float* out = ZOUT(0);

    // distribution choices for amp and dur and constants of distribution
    int whichamp = (int)ZIN0(0);
    int whichdur = (int)ZIN0(1);
    float aamp = ZIN0(2);
    float adur = ZIN0(3);
    float freq = ZIN0(4);
    float scaleamp = ZIN0(5);
    float scaledur = ZIN0(6);

    double phase = unit->mPhase;
    float amp = unit->mAmp;
    float nextamp = unit->mNextAmp;
    float speed = unit->mSpeed;
    int index = unit->mIndex;
    int interpmult = (int)unit->mInterpMult;
    double lastphase = unit->mLastPhase;
    double nextphase = unit->mNextPhase;

    RGen& rgen = *unit->mParent->mRGen;

    float* amplist = unit->mAmpList;
    double* phaselist = unit->mPhaseList;

    LOOP1(
        inNumSamples,
        float z;

        if (phase >= 1.) { // calculate all targets for new period
            phase -= 1.;

            int num = (int)(ZIN0(8));
            if ((num > (unit->mMemorySize)) || (num < 1))
                num = unit->mMemorySize;

            float dursum = 0.0f;

            float* memoryamp = unit->mMemoryAmp;
            float* memorydur = unit->mMemoryDur;

            for (int j = 0; j < num; ++j) {
                if (j > 0) { // first BP always stays at 0
                    float amp = (memoryamp[j]) + (scaleamp * Gendyn_distribution(whichamp, aamp, rgen.frand()));
                    amp = Gendyn_mirroring(-1.0f, 1.0f, amp);
                    memoryamp[j] = amp;
                }

                float dur = (memorydur[j]) + (scaledur * Gendyn_distribution(whichdur, adur, rgen.frand()));
                dur = Gendyn_mirroring(0.01f, 1.0f, dur); // will get normalised in a moment, don't allow zeroes
                memorydur[j] = dur;
                dursum += dur;
            }

            // normalising constant
            dursum = 1.f / dursum;

            int active = 0;

            // phase duration of a sample
            float minphase = unit->mFreqMul;

            speed = freq * minphase;

            // normalise and discard any too short (even first)
            for (int j = 0; j < num; ++j) {
                float dur = memorydur[j];
                dur *= dursum;

                if (dur >= minphase) {
                    amplist[active] = memoryamp[j];
                    phaselist[active] = dur;
                    ++active;
                }
            }

            // add a zero on the end at active
            amplist[active] = 0.0f; // guard element
            phaselist[active] = 2.0; // safety element

            // lastphase=0.0;
            //					nextphase= phaselist[0];
            //					amp=amplist[0];
            //					nextamp=amplist[1];
            //					index=0;
            //					unit->mIndex=index;
            //
            // setup to trigger next block
            nextphase = 0.0;
            nextamp = amplist[0];
            index = -1;
        }

        if (phase >= nextphase) { // are we into a new region?
            // new code for indexing
            ++index; //=index+1; //%num;

            amp = nextamp;

            unit->mIndex = index;

            lastphase = nextphase;
            nextphase = lastphase + phaselist[index];
            nextamp = amplist[index + 1];

            interpmult = (int)(1.0 / (nextphase - lastphase));
        }

        float interp = (phase - lastphase) * interpmult;

        // linear interpolation could be changed
        z = ((1.0f - interp) * amp) + (interp * nextamp);

        phase += speed; ZXP(out) = z;);

    unit->mPhase = phase;
    unit->mSpeed = speed;
    unit->mInterpMult = interpmult;
    unit->mAmp = amp;
    unit->mNextAmp = nextamp;
    unit->mLastPhase = lastphase;
    unit->mNextPhase = nextphase;
}

PluginLoad(Gendyn) {
    ft = inTable;

    DefineDtorUnit(Gendy1);
    DefineDtorUnit(Gendy2);
    DefineDtorUnit(Gendy3);
}