/*
    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
*/

#include "SC_PlugIn.h"
#include <cstdio>
#include <cmath>
#include <limits>

using std::floor;
using std::numeric_limits;

static InterfaceTable* ft;

struct Demand : public Unit {
    float m_prevtrig;
    float m_prevreset;
    float* m_prevout;
    float** m_out;
};

struct Duty : public Unit {
    float m_count;
    float m_prevreset;
    float m_prevout;
};

struct DemandEnvGen : public Unit {
    float m_phase;
    float m_prevreset;

    double m_a1, m_a2, m_b1, m_y1, m_y2, m_grow, m_level, m_endLevel, m_curve;
    int m_shape;
    bool m_release, m_running;
};

struct TDuty : public Unit {
    float m_count;
    float m_prevreset;
};


struct Dseries : public Unit {
    double m_repeats;
    int32 m_repeatCount;
    double m_value;
    double m_step;
};

struct Dgeom : public Unit {
    double m_repeats;
    int32 m_repeatCount;
    double m_value;
    double m_grow;
};

struct Dwhite : public Unit {
    double m_repeats;
    int32 m_repeatCount;
    float m_lo;
    float m_range;
};

struct Dbrown : public Unit {
    double m_repeats;
    int32 m_repeatCount;
    float m_lo;
    float m_hi;
    float m_step;
    float m_val;
};

struct Diwhite : public Unit {
    double m_repeats;
    int32 m_repeatCount;
    int32 m_lo;
    int32 m_range;
};

struct Dibrown : public Unit {
    double m_repeats;
    int32 m_repeatCount;
    int32 m_lo;
    int32 m_hi;
    int32 m_step;
    int32 m_val;
};

struct Dseq : public Unit {
    double m_repeats;
    int32 m_repeatCount;
    int32 m_index;
    bool m_needToResetChild;
};

struct Dshuf : public Unit {
    double m_repeats;
    int32 m_repeatCount;
    int32 m_index;
    bool m_needToResetChild;
    int32* m_indices;
};

struct Dbufrd : public Unit {
    float m_fbufnum;
    SndBuf* m_buf;
};

struct Dbufwr : public Unit {
    float m_fbufnum;
    SndBuf* m_buf;
};

struct Dser : public Dseq {};

struct Drand : public Dseq {};

struct Dxrand : public Dseq {};

struct Dwrand : public Dseq {
    int32 m_weights_size;
};

struct Dswitch1 : public Unit {};

struct Dswitch : public Unit {
    int m_index;
};

struct Dstutter : public Unit {
    double m_repeats;
    double m_repeatCount;
    float m_value;
};

struct Ddup : public Unit {
    double m_repeats;
    double m_repeatCount;
    float m_value;
};

struct Dpoll : public Unit {
    char* m_id_string;
    bool m_mayprint;
    float m_id;
};

struct Dreset : public Unit {
    float prev_reset;
};

struct Dconst : public Unit {
    float m_total;
    float m_runningsum;
    float m_tolerance;
};

extern "C" {


void Demand_Ctor(Demand* unit);
void Demand_Dtor(Demand* unit);
void Demand_next_aa(Demand* unit, int inNumSamples);
void Demand_next_ak(Demand* unit, int inNumSamples);
void Demand_next_ka(Demand* unit, int inNumSamples);

void Duty_Ctor(Duty* unit);
void Duty_next_da(Duty* unit, int inNumSamples);
void Duty_next_dk(Duty* unit, int inNumSamples);
void Duty_next_dd(Duty* unit, int inNumSamples);

void TDuty_Ctor(TDuty* unit);
void TDuty_next_da(TDuty* unit, int inNumSamples);
void TDuty_next_dk(TDuty* unit, int inNumSamples);
void TDuty_next_dd(TDuty* unit, int inNumSamples);

void DemandEnvGen_Ctor(DemandEnvGen* unit);
void DemandEnvGen_next_k(DemandEnvGen* unit, int inNumSamples);
void DemandEnvGen_next_a(DemandEnvGen* unit, int inNumSamples);

void Dseries_Ctor(Dseries* unit);
void Dseries_next(Dseries* unit, int inNumSamples);

void Dgeom_Ctor(Dgeom* unit);
void Dgeom_next(Dgeom* unit, int inNumSamples);

void Dwhite_Ctor(Dwhite* unit);
void Dwhite_next(Dwhite* unit, int inNumSamples);

void Dbrown_Ctor(Dbrown* unit);
void Dbrown_next(Dbrown* unit, int inNumSamples);

void Diwhite_Ctor(Diwhite* unit);
void Diwhite_next(Diwhite* unit, int inNumSamples);

void Dibrown_Ctor(Dibrown* unit);
void Dibrown_next(Dibrown* unit, int inNumSamples);

void Dseq_Ctor(Dseq* unit);
void Dseq_next(Dseq* unit, int inNumSamples);

void Dshuf_Ctor(Dshuf* unit);
void Dshuf_Dtor(Dshuf* unit);
void Dshuf_next(Dshuf* unit, int inNumSamples);


void Dbufrd_Ctor(Dbufrd* unit);
void Dbufrd_next(Dbufrd* unit, int inNumSamples);

void Dbufwr_Ctor(Dbufwr* unit);
void Dbufwr_next(Dbufwr* unit, int inNumSamples);

void Dser_Ctor(Dser* unit);
void Dser_next(Dser* unit, int inNumSamples);

void Drand_Ctor(Drand* unit);
void Drand_next(Drand* unit, int inNumSamples);

void Dxrand_Ctor(Dxrand* unit);
void Dxrand_next(Dxrand* unit, int inNumSamples);

void Dwrand_Ctor(Dwrand* unit);
void Dwrand_next(Dwrand* unit, int inNumSamples);

void Dswitch1_Ctor(Dswitch1* unit);
void Dswitch1_next(Dswitch1* unit, int inNumSamples);

void Dswitch_Ctor(Dswitch* unit);
void Dswitch_next(Dswitch* unit, int inNumSamples);

void Dstutter_Ctor(Dstutter* unit);
void Dstutter_next(Dstutter* unit, int inNumSamples);

void Ddup_Ctor(Ddup* unit);
void Ddup_next(Ddup* unit, int inNumSamples);

void Dpoll_Ctor(Dpoll* unit);
void Dpoll_Ctor(Dpoll* unit);
void Dpoll_next(Dpoll* unit, int inNumSamples);

void Dreset_Ctor(Dreset* unit);
void Dreset_next(Dreset* unit, int inNumSamples);

void Dconst_Ctor(Dconst* unit);
void Dconst_next(Dconst* unit, int inNumSamples);
};


void Demand_next_aa(Demand* unit, int inNumSamples) {
    float* trig = ZIN(0);
    float* reset = ZIN(1);

    float** out = unit->m_out;
    float* prevout = unit->m_prevout;

    for (int i = 0; i < unit->mNumOutputs; ++i) {
        out[i] = OUT(i);
    }

    float prevtrig = unit->m_prevtrig;
    float prevreset = unit->m_prevreset;

    // Print("Demand_next_aa %d  %g\n", inNumSamples, prevtrig);
    for (int i = 0; i < inNumSamples; ++i) {
        float ztrig = ZXP(trig);
        float zreset = ZXP(reset);
        if (zreset > 0.f && prevreset <= 0.f) {
            for (int j = 2; j < unit->mNumInputs; ++j) {
                RESETINPUT(j);
            }
        }
        if (ztrig > 0.f && prevtrig <= 0.f) {
            // Print("triggered\n");
            for (int j = 2, k = 0; j < unit->mNumInputs; ++j, ++k) {
                float x = DEMANDINPUT_A(j, i + 1);
                // printf("in  %d %g\n", k, x);
                if (sc_isnan(x)) {
                    x = prevout[k];
                    unit->mDone = true;
                } else
                    prevout[k] = x;
                out[k][i] = x;
            }
        } else {
            for (int j = 2, k = 0; j < unit->mNumInputs; ++j, ++k) {
                out[k][i] = prevout[k];
            }
        }
        prevtrig = ztrig;
        prevreset = zreset;
    }

    unit->m_prevtrig = prevtrig;
    unit->m_prevreset = prevreset;
}


void Demand_next_ak(Demand* unit, int inNumSamples) {
    float* trig = ZIN(0);
    float zreset = IN0(1);

    float** out = unit->m_out;
    float* prevout = unit->m_prevout;

    for (int i = 0; i < unit->mNumOutputs; ++i) {
        out[i] = OUT(i);
    }

    float prevtrig = unit->m_prevtrig;
    float prevreset = unit->m_prevreset;

    for (int i = 0; i < inNumSamples; ++i) {
        float ztrig = ZXP(trig);
        if (zreset > 0.f && prevreset <= 0.f) {
            for (int j = 2; j < unit->mNumInputs; ++j) {
                RESETINPUT(j);
            }
        }

        if (ztrig > 0.f && prevtrig <= 0.f) {
            for (int j = 2, k = 0; j < unit->mNumInputs; ++j, ++k) {
                float x = DEMANDINPUT_A(j, i + 1);
                if (sc_isnan(x)) {
                    x = prevout[k];
                    unit->mDone = true;
                } else
                    prevout[k] = x;
                out[k][i] = x;
            }

        } else {
            for (int j = 2, k = 0; j < unit->mNumInputs; ++j, ++k) {
                out[k][i] = prevout[k];
            }
        }
        prevtrig = ztrig;
        prevreset = zreset;
    }

    unit->m_prevtrig = prevtrig;
    unit->m_prevreset = prevreset;
}


void Demand_next_ka(Demand* unit, int inNumSamples) {
    float ztrig = IN0(0);
    float* reset = ZIN(1);

    float** out = unit->m_out;
    float* prevout = unit->m_prevout;

    for (int i = 0; i < unit->mNumOutputs; ++i) {
        out[i] = OUT(i);
    }

    float prevtrig = unit->m_prevtrig;
    float prevreset = unit->m_prevreset;

    for (int i = 0; i < inNumSamples; ++i) {
        float zreset = ZXP(reset);
        if (zreset > 0.f && prevreset <= 0.f) {
            for (int j = 2; j < unit->mNumInputs; ++j) {
                RESETINPUT(j);
            }
        }
        if (ztrig > 0.f && prevtrig <= 0.f) {
            for (int j = 2, k = 0; j < unit->mNumInputs; ++j, ++k) {
                float x = DEMANDINPUT_A(j, i + 1);
                if (sc_isnan(x)) {
                    x = prevout[k];
                    unit->mDone = true;
                } else
                    prevout[k] = x;
                out[k][i] = x;
            }
        }
        prevtrig = ztrig;
        prevreset = zreset;
    }

    unit->m_prevtrig = prevtrig;
    unit->m_prevreset = prevreset;
}


void Demand_Ctor(Demand* unit) {
    // Print("Demand_Ctor\n");
    if (INRATE(0) == calc_FullRate) {
        if (INRATE(1) == calc_FullRate) {
            SETCALC(Demand_next_aa);
        } else {
            SETCALC(Demand_next_ak);
        }
    } else {
        if (INRATE(1) == calc_FullRate) {
            SETCALC(Demand_next_ka);
        } else {
            SETCALC(Demand_next_aa);
        }
    }

    for (int i = 0; i < unit->mNumOutputs; ++i)
        OUT0(i) = 0.f;

    char* memoryChunk = (char*)RTAlloc(unit->mWorld, unit->mNumOutputs * (sizeof(float) + sizeof(float*)));
    ClearUnitIfMemFailed(memoryChunk);

    unit->m_prevout = (float*)memoryChunk;
    unit->m_out = (float**)(memoryChunk + unit->mNumOutputs * sizeof(float));

    unit->m_prevtrig = 0.f;
    unit->m_prevreset = 0.f;
    std::fill_n(unit->m_prevout, unit->mNumOutputs, 0.f);
}

void Demand_Dtor(Demand* unit) {
    if (unit->m_prevout)
        RTFree(unit->mWorld, unit->m_prevout);
}


/////////////////////////////////////////////////////////////////////////////

enum { duty_dur, duty_reset, duty_doneAction, duty_level };

void Duty_next_da(Duty* unit, int inNumSamples) {
    float* reset = ZIN(duty_reset);

    float* out = OUT(0);
    float prevout = unit->m_prevout;
    float count = unit->m_count;
    float prevreset = unit->m_prevreset;
    float sr = (float)SAMPLERATE;

    for (int i = 0; i < inNumSamples; ++i) {
        float zreset = ZXP(reset);
        if (zreset > 0.f && prevreset <= 0.f) {
            RESETINPUT(duty_level);
            RESETINPUT(duty_dur);
            count = 0.f;
        }
        if (count <= 0.f) {
            count = DEMANDINPUT_A(duty_dur, i + 1) * sr + count;
            if (sc_isnan(count)) {
                int doneAction = (int)ZIN0(duty_doneAction);
                DoneAction(doneAction, unit);
            }
            float x = DEMANDINPUT_A(duty_level, i + 1);
            // printf("in  %d\n", count);
            if (sc_isnan(x)) {
                x = prevout;
                int doneAction = (int)ZIN0(duty_doneAction);
                DoneAction(doneAction, unit);
            } else {
                prevout = x;
            }
            out[i] = x;

        } else {
            out[i] = prevout;
        }
        count--;
        prevreset = zreset;
    }

    unit->m_count = count;
    unit->m_prevreset = prevreset;
    unit->m_prevout = prevout;
}

void Duty_next_dk(Duty* unit, int inNumSamples) {
    float zreset = ZIN0(duty_reset);

    float* out = OUT(0);
    float prevout = unit->m_prevout;
    float count = unit->m_count;
    float prevreset = unit->m_prevreset;
    float sr = (float)SAMPLERATE;

    for (int i = 0; i < inNumSamples; ++i) {
        if (zreset > 0.f && prevreset <= 0.f) {
            RESETINPUT(duty_level);
            RESETINPUT(duty_dur);
            count = 0.f;
        }
        if (count <= 0.f) {
            count = DEMANDINPUT_A(duty_dur, i + 1) * sr + count;
            if (sc_isnan(count)) {
                int doneAction = (int)ZIN0(duty_doneAction);
                DoneAction(doneAction, unit);
            }

            float x = DEMANDINPUT_A(duty_level, i + 1);
            if (sc_isnan(x)) {
                x = prevout;
                int doneAction = (int)ZIN0(duty_doneAction);
                DoneAction(doneAction, unit);
            } else {
                prevout = x;
            }

            out[i] = x;

        } else {
            out[i] = prevout;
        }
        count--;
        prevreset = zreset;
    }

    unit->m_count = count;
    unit->m_prevreset = prevreset;
    unit->m_prevout = prevout;
}


void Duty_next_dd(Duty* unit, int inNumSamples) {
    float* out = OUT(0);
    float prevout = unit->m_prevout;
    float count = unit->m_count;
    float reset = unit->m_prevreset;
    float sr = (float)SAMPLERATE;

    for (int i = 0; i < inNumSamples; ++i) {
        if (reset <= 0.f) {
            RESETINPUT(duty_level);
            RESETINPUT(duty_dur);
            count = 0.f;
            reset = DEMANDINPUT_A(duty_reset, i + 1) * sr + reset;
        } else {
            reset--;
        }
        if (count <= 0.f) {
            count = DEMANDINPUT_A(duty_dur, i + 1) * sr + count;
            if (sc_isnan(count)) {
                int doneAction = (int)ZIN0(duty_doneAction);
                DoneAction(doneAction, unit);
            }
            float x = DEMANDINPUT_A(duty_level, i + 1);
            // printf("in  %d %g\n", k, x);
            if (sc_isnan(x)) {
                x = prevout;
                int doneAction = (int)ZIN0(duty_doneAction);
                DoneAction(doneAction, unit);
            } else {
                prevout = x;
            }
        }

        out[i] = prevout;
        count--;
    }

    unit->m_count = count;
    unit->m_prevreset = reset;
    unit->m_prevout = prevout;
}


void Duty_Ctor(Duty* unit) {
    // DEMANDINPUT_A is not needed here, because we are at init time.

    if (INRATE(duty_reset) == calc_FullRate) {
        SETCALC(Duty_next_da);
        unit->m_prevreset = 0.f;

    } else {
        if (INRATE(duty_reset) == calc_DemandRate) {
            SETCALC(Duty_next_dd);
            unit->m_prevreset = DEMANDINPUT(duty_reset) * SAMPLERATE;
        } else {
            SETCALC(Duty_next_dk);
            unit->m_prevreset = 0.f;
        }
    }

    unit->m_count = DEMANDINPUT(duty_dur) * SAMPLERATE;
    unit->m_prevout = DEMANDINPUT(duty_level);
    OUT0(0) = unit->m_prevout;
}


/////////////////////////////////////////////////////////////////////////////

enum {
    d_env_level,
    d_env_dur,
    d_env_shape,
    d_env_curve,
    d_env_gate,
    d_env_reset,
    d_env_levelScale,
    d_env_levelBias,
    d_env_timeScale,
    d_env_doneAction

};

enum {
    shape_Step,
    shape_Linear,
    shape_Exponential,
    shape_Sine,
    shape_Welch,
    shape_Curve,
    shape_Squared,
    shape_Cubed,
    shape_Sustain = 9999
};


void DemandEnvGen_next_k(DemandEnvGen* unit, int inNumSamples) {
    float zreset = ZIN0(d_env_reset);

    float* out = ZOUT(0);
    double level = unit->m_level;
    float phase = unit->m_phase;
    double curve = unit->m_curve;
    bool release = unit->m_release;
    bool running = unit->m_running;
    int shape = unit->m_shape;

    // printf("phase %f level %f \n", phase, level);

    for (int i = 0; i < inNumSamples; ++i) {
        if (zreset > 0.f && unit->m_prevreset <= 0.f) {
            // printf("reset: %f %f \n", zreset, unit->m_prevreset);
            RESETINPUT(d_env_level);
            RESETINPUT(d_env_dur);
            RESETINPUT(d_env_shape);
            RESETINPUT(d_env_curve);

            if (zreset <= 1.f) {
                DEMANDINPUT(d_env_level); // remove first level
            } else {
                level = DEMANDINPUT(d_env_level); // jump to first level
            }

            release = false;
            running = true;
            phase = 0.f;
        }


        if (phase <= 0.f && running) {
            // was a release during last segment?
            if (release) {
                running = false;
                release = false;
                // printf("release: %f %f \n", phase, level);
                int doneAction = (int)ZIN0(d_env_doneAction);
                DoneAction(doneAction, unit);

            } else {
                // new time

                float dur = DEMANDINPUT(d_env_dur);
                // printf("dur: %f \n", dur);
                if (sc_isnan(dur)) {
                    release = true;
                    running = false;
                    phase = numeric_limits<float>::max();
                } else {
                    phase = dur * ZIN0(d_env_timeScale) * SAMPLERATE + phase;
                }


                // new shape
                float fshape = DEMANDINPUT(d_env_shape);
                if (sc_isnan(fshape))
                    shape = unit->m_shape;
                else
                    shape = (int)fshape;

                curve = DEMANDINPUT(d_env_curve);
                if (sc_isnan(curve))
                    curve = unit->m_curve;

                float count;
                if (phase <= 1.f) {
                    shape = 1; // shape_Linear
                    count = 1.f;
                } else {
                    count = phase;
                }
                if (dur * 0.5f < SAMPLEDUR)
                    shape = 1;

                // printf("shape: %i, curve: %f, dur: %f \n", shape, curve, dur);


                // new end level

                double endLevel = DEMANDINPUT(d_env_level);
                // printf("levels: %f %f\n", level, endLevel);
                if (sc_isnan(endLevel)) {
                    endLevel = unit->m_endLevel;
                    release = true;
                    phase = 0.f;
                    shape = 0;
                } else {
                    endLevel = endLevel * ZIN0(d_env_levelScale) + ZIN0(d_env_levelBias);
                    unit->m_endLevel = endLevel;
                }


                // calculate shape parameters

                switch (shape) {
                case shape_Step: {
                    level = endLevel;
                } break;
                case shape_Linear: {
                    unit->m_grow = (endLevel - level) / count;
                } break;
                case shape_Exponential: {
                    unit->m_grow = pow(endLevel / level, 1.0 / count);
                } break;
                case shape_Sine: {
                    double w = pi / count;

                    unit->m_a2 = (endLevel + level) * 0.5;
                    unit->m_b1 = 2. * cos(w);
                    unit->m_y1 = (endLevel - level) * 0.5;
                    unit->m_y2 = unit->m_y1 * sin(pi * 0.5 - w);
                    level = unit->m_a2 - unit->m_y1;
                } break;
                case shape_Welch: {
                    double w = (pi * 0.5) / count;

                    unit->m_b1 = 2. * cos(w);

                    if (endLevel >= level) {
                        unit->m_a2 = level;
                        unit->m_y1 = 0.;
                        unit->m_y2 = -sin(w) * (endLevel - level);
                    } else {
                        unit->m_a2 = endLevel;
                        unit->m_y1 = level - endLevel;
                        unit->m_y2 = cos(w) * (level - endLevel);
                    }
                    level = unit->m_a2 + unit->m_y1;
                } break;
                case shape_Curve: {
                    if (fabs(curve) < 0.001) {
                        unit->m_shape = shape = 1; // shape_Linear
                        unit->m_grow = (endLevel - level) / count;
                    } else {
                        double a1 = (endLevel - level) / (1.0 - exp(curve));
                        unit->m_a2 = level + a1;
                        unit->m_b1 = a1;
                        unit->m_grow = exp(curve / ceil(count));
                    }
                } break;
                case shape_Squared: {
                    unit->m_y1 = sqrt(level);
                    unit->m_y2 = sqrt(endLevel);
                    unit->m_grow = (unit->m_y2 - unit->m_y1) / count;
                } break;
                case shape_Cubed: {
                    unit->m_y1 = pow(level, 0.33333333);
                    unit->m_y2 = pow(endLevel, 0.33333333);
                    unit->m_grow = (unit->m_y2 - unit->m_y1) / count;
                } break;
                }
            }
        }


        if (running) {
            switch (shape) {
            case shape_Step: {
            } break;
            case shape_Linear: {
                double grow = unit->m_grow;
                // Print("level %g\n", level);
                level += grow;
            } break;
            case shape_Exponential: {
                double grow = unit->m_grow;
                level *= grow;
            } break;
            case shape_Sine: {
                double a2 = unit->m_a2;
                double b1 = unit->m_b1;
                double y2 = unit->m_y2;
                double y1 = unit->m_y1;
                double y0 = b1 * y1 - y2;
                level = a2 - y0;
                y2 = y1;
                y1 = y0;
                unit->m_y1 = y1;
                unit->m_y2 = y2;
            } break;
            case shape_Welch: {
                double a2 = unit->m_a2;
                double b1 = unit->m_b1;
                double y2 = unit->m_y2;
                double y1 = unit->m_y1;
                double y0 = b1 * y1 - y2;
                level = a2 + y0;
                y2 = y1;
                y1 = y0;
                unit->m_y1 = y1;
                unit->m_y2 = y2;
            } break;
            case shape_Curve: {
                double a2 = unit->m_a2;
                double b1 = unit->m_b1;
                double grow = unit->m_grow;
                b1 *= grow;
                level = a2 - b1;
                unit->m_b1 = b1;
            } break;
            case shape_Squared: {
                double grow = unit->m_grow;
                double y1 = unit->m_y1;
                y1 += grow;
                level = y1 * y1;
                unit->m_y1 = y1;
            } break;
            case shape_Cubed: {
                double grow = unit->m_grow;
                double y1 = unit->m_y1;
                y1 += grow;
                level = y1 * y1 * y1;
                unit->m_y1 = y1;
            } break;
            case shape_Sustain: {
            } break;
            }

            phase--;
        }


        ZXP(out) = level;
    }
    float zgate = ZIN0(d_env_gate);
    if (zgate >= 1.f) {
        unit->m_running = true;
    } else if (zgate > 0.f) {
        unit->m_running = true;
        release = true; // release next time.
    } else {
        unit->m_running = false; // sample and hold
    }

    unit->m_level = level;
    unit->m_curve = curve;
    unit->m_shape = shape;
    unit->m_prevreset = zreset;
    unit->m_release = release;

    unit->m_phase = phase;
}


void DemandEnvGen_next_a(DemandEnvGen* unit, int inNumSamples) {
    float* reset = ZIN(d_env_reset);
    float* gate = ZIN(d_env_gate);

    float* out = ZOUT(0);

    float prevreset = unit->m_prevreset;
    double level = unit->m_level;
    float phase = unit->m_phase;
    double curve = unit->m_curve;
    bool release = unit->m_release;
    bool running = unit->m_running;


    int shape = unit->m_shape;
    // printf("phase %f \n", phase);

    for (int i = 0; i < inNumSamples; ++i) {
        float zreset = ZXP(reset);
        if (zreset > 0.f && prevreset <= 0.f) {
            // printf("reset: %f %f \n", zreset, unit->m_prevreset);
            RESETINPUT(d_env_level);
            if (zreset <= 1.f) {
                DEMANDINPUT_A(d_env_level, i + 1); // remove first level
            } else {
                level = DEMANDINPUT_A(d_env_level, i + 1); // jump to first level
            }

            RESETINPUT(d_env_dur);
            RESETINPUT(d_env_shape);
            release = false;
            running = true;

            phase = 0.f;
        }

        prevreset = zreset;


        if (phase <= 0.f && running) {
            // was a release?
            if (release) {
                running = false;
                release = false;
                // printf("release: %f %f \n", phase, level);
                int doneAction = (int)ZIN0(d_env_doneAction);
                DoneAction(doneAction, unit);

            } else {
                // new time

                float dur = DEMANDINPUT_A(d_env_dur, i + 1);
                // printf("dur: %f \n", dur);
                if (sc_isnan(dur)) {
                    release = true;
                    running = false;
                    phase = numeric_limits<float>::max();
                } else {
                    phase = dur * ZIN0(d_env_timeScale) * SAMPLERATE + phase;
                }

                // new shape
                float count;
                curve = DEMANDINPUT_A(d_env_curve, i + 1);

                // printf("shapes: %i \n", shape);
                if (sc_isnan(curve))
                    curve = unit->m_curve;

                float fshape = DEMANDINPUT_A(d_env_shape, i + 1);
                if (sc_isnan(fshape))
                    shape = unit->m_shape;
                else
                    shape = (int)fshape;

                if (phase <= 1.f) {
                    shape = 1; // shape_Linear
                    count = 1.f;
                } else {
                    count = phase;
                }
                if (dur * 0.5f < SAMPLEDUR)
                    shape = 1;


                // new end level

                double endLevel = DEMANDINPUT_A(d_env_level, i + 1);
                // printf("levels: %f %f\n", level, endLevel);
                if (sc_isnan(endLevel)) {
                    endLevel = unit->m_endLevel;
                    release = true;
                    phase = 0.f;
                    shape = 0;
                } else {
                    endLevel = endLevel * ZIN0(d_env_levelScale) + ZIN0(d_env_levelBias);
                    unit->m_endLevel = endLevel;
                }


                // calculate shape parameters

                switch (shape) {
                case shape_Step: {
                    level = endLevel;
                } break;
                case shape_Linear: {
                    unit->m_grow = (endLevel - level) / count;
                } break;
                case shape_Exponential: {
                    unit->m_grow = pow(endLevel / level, 1.0 / count);
                } break;
                case shape_Sine: {
                    double w = pi / count;

                    unit->m_a2 = (endLevel + level) * 0.5;
                    unit->m_b1 = 2. * cos(w);
                    unit->m_y1 = (endLevel - level) * 0.5;
                    unit->m_y2 = unit->m_y1 * sin(pi * 0.5 - w);
                    level = unit->m_a2 - unit->m_y1;
                } break;
                case shape_Welch: {
                    double w = (pi * 0.5) / count;

                    unit->m_b1 = 2. * cos(w);

                    if (endLevel >= level) {
                        unit->m_a2 = level;
                        unit->m_y1 = 0.;
                        unit->m_y2 = -sin(w) * (endLevel - level);
                    } else {
                        unit->m_a2 = endLevel;
                        unit->m_y1 = level - endLevel;
                        unit->m_y2 = cos(w) * (level - endLevel);
                    }
                    level = unit->m_a2 + unit->m_y1;
                } break;
                case shape_Curve: {
                    if (fabs(curve) < 0.001) {
                        unit->m_shape = shape = 1; // shape_Linear
                        unit->m_grow = (endLevel - level) / count;
                    } else {
                        double a1 = (endLevel - level) / (1.0 - exp(curve));
                        unit->m_a2 = level + a1;
                        unit->m_b1 = a1;
                        unit->m_grow = exp(curve / ceil(count));
                    }
                } break;
                case shape_Squared: {
                    unit->m_y1 = sqrt(level);
                    unit->m_y2 = sqrt(endLevel);
                    unit->m_grow = (unit->m_y2 - unit->m_y1) / count;
                } break;
                case shape_Cubed: {
                    unit->m_y1 = pow(level, 0.33333333);
                    unit->m_y2 = pow(endLevel, 0.33333333);
                    unit->m_grow = (unit->m_y2 - unit->m_y1) / count;
                } break;
                }
            }
        }


        if (running) {
            switch (shape) {
            case shape_Step: {
            } break;
            case shape_Linear: {
                double grow = unit->m_grow;
                // Print("level %g\n", level);
                level += grow;
            } break;
            case shape_Exponential: {
                double grow = unit->m_grow;
                level *= grow;
            } break;
            case shape_Sine: {
                double a2 = unit->m_a2;
                double b1 = unit->m_b1;
                double y2 = unit->m_y2;
                double y1 = unit->m_y1;
                double y0 = b1 * y1 - y2;
                level = a2 - y0;
                y2 = y1;
                y1 = y0;
                unit->m_y1 = y1;
                unit->m_y2 = y2;
            } break;
            case shape_Welch: {
                double a2 = unit->m_a2;
                double b1 = unit->m_b1;
                double y2 = unit->m_y2;
                double y1 = unit->m_y1;
                double y0 = b1 * y1 - y2;
                level = a2 + y0;
                y2 = y1;
                y1 = y0;
                unit->m_y1 = y1;
                unit->m_y2 = y2;
            } break;
            case shape_Curve: {
                double a2 = unit->m_a2;
                double b1 = unit->m_b1;
                double grow = unit->m_grow;
                b1 *= grow;
                level = a2 - b1;
                unit->m_b1 = b1;
            } break;
            case shape_Squared: {
                double grow = unit->m_grow;
                double y1 = unit->m_y1;
                y1 += grow;
                level = y1 * y1;
                unit->m_y1 = y1;
            } break;
            case shape_Cubed: {
                double grow = unit->m_grow;
                double y1 = unit->m_y1;
                y1 += grow;
                level = y1 * y1 * y1;
                unit->m_y1 = y1;
            } break;
            case shape_Sustain: {
            } break;
            }

            phase--;
        }

        ZXP(out) = level;
        float zgate = ZXP(gate);

        if (zgate >= 1.f) {
            unit->m_running = true;
        } else if (zgate > 0.f) {
            unit->m_running = true;
            release = true; // release next time.
        } else {
            unit->m_running = false; // sample and hold
        }
    }

    unit->m_level = level;
    unit->m_curve = curve;
    unit->m_shape = shape;
    unit->m_prevreset = prevreset;
    unit->m_release = release;
    unit->m_phase = phase;
}


void DemandEnvGen_Ctor(DemandEnvGen* unit) {
    // derive the first level.

    unit->m_level = DEMANDINPUT(d_env_level);
    if (sc_isnan(unit->m_level)) {
        unit->m_level = 0.f;
    }
    unit->m_endLevel = unit->m_level;
    unit->m_release = false;
    unit->m_prevreset = 0.f;
    unit->m_phase = 0.f;
    unit->m_running = ZIN0(d_env_gate) > 0.f;

    if (INRATE(d_env_gate) == calc_FullRate) {
        SETCALC(DemandEnvGen_next_a);
    } else {
        SETCALC(DemandEnvGen_next_k);
    }

    DemandEnvGen_next_k(unit, 1);
}


/////////////////////////////////////////////////////////////////////////////

void TDuty_next_da(TDuty* unit, int inNumSamples) {
    float* reset = ZIN(duty_reset);
    float* out = OUT(0);

    float count = unit->m_count;
    float prevreset = unit->m_prevreset;
    float sr = (float)SAMPLERATE;

    for (int i = 0; i < inNumSamples; ++i) {
        float zreset = ZXP(reset);
        if (zreset > 0.f && prevreset <= 0.f) {
            RESETINPUT(duty_level);
            RESETINPUT(duty_dur);
            count = 0.f;
        }
        if (count <= 0.f) {
            count = DEMANDINPUT_A(duty_dur, i + 1) * sr + count;
            if (sc_isnan(count)) {
                int doneAction = (int)ZIN0(2);
                DoneAction(doneAction, unit);
            }
            float x = DEMANDINPUT_A(duty_level, i + 1);
            // printf("in  %d %g\n", k, x);
            if (sc_isnan(x))
                x = 0.f;
            out[i] = x;
        } else {
            out[i] = 0.f;
        }
        count--;
        prevreset = zreset;
    }

    unit->m_count = count;
    unit->m_prevreset = prevreset;
}

void TDuty_next_dk(TDuty* unit, int inNumSamples) {
    float zreset = ZIN0(duty_reset);

    float* out = OUT(0);
    float count = unit->m_count;
    float prevreset = unit->m_prevreset;
    float sr = (float)SAMPLERATE;

    for (int i = 0; i < inNumSamples; ++i) {
        if (zreset > 0.f && prevreset <= 0.f) {
            RESETINPUT(duty_level);
            RESETINPUT(duty_dur);
            count = 0.f;
        }
        if (count <= 0.f) {
            count = DEMANDINPUT_A(duty_dur, i + 1) * sr + count;
            if (sc_isnan(count)) {
                int doneAction = (int)ZIN0(2);
                DoneAction(doneAction, unit);
            }
            float x = DEMANDINPUT_A(duty_level, i + 1);
            // printf("in  %d %g\n", k, x);
            if (sc_isnan(x))
                x = 0.f;
            out[i] = x;
        } else {
            out[i] = 0.f;
        }
        count--;
        prevreset = zreset;
    }

    unit->m_count = count;
    unit->m_prevreset = prevreset;
}


void TDuty_next_dd(TDuty* unit, int inNumSamples) {
    float* out = OUT(0);
    float count = unit->m_count;
    float reset = unit->m_prevreset;
    float sr = (float)SAMPLERATE;

    for (int i = 0; i < inNumSamples; ++i) {
        if (reset <= 0.f) {
            RESETINPUT(duty_level);
            RESETINPUT(duty_dur);
            count = 0.f;
            reset = DEMANDINPUT_A(duty_reset, i + 1) * sr + reset;
        } else {
            reset--;
        }
        if (count <= 0.f) {
            count = DEMANDINPUT_A(duty_dur, i + 1) * sr + count;
            if (sc_isnan(count)) {
                int doneAction = (int)ZIN0(2);
                DoneAction(doneAction, unit);
            }
            float x = DEMANDINPUT_A(duty_level, i + 1);
            // printf("in  %d %g\n", k, x);
            if (sc_isnan(x))
                x = 0.f;
            out[i] = x;
        } else {
            out[i] = 0.f;
        }
        count--;
    }

    unit->m_count = count;
    unit->m_prevreset = reset;
}


void TDuty_Ctor(TDuty* unit) {
    // DEMANDINPUT_A is not needed here, because we are at init time.
    if (INRATE(1) == calc_FullRate) {
        SETCALC(TDuty_next_da);
        unit->m_prevreset = 0.f;

    } else {
        if (INRATE(1) == calc_DemandRate) {
            SETCALC(TDuty_next_dd);
            unit->m_prevreset = DEMANDINPUT(duty_reset) * SAMPLERATE;
        } else {
            SETCALC(TDuty_next_dk);
            unit->m_prevreset = 0.f;
        }
    }
    // support for gap-first.
    if (IN0(4)) {
        unit->m_count = DEMANDINPUT(duty_dur) * SAMPLERATE;
    } else {
        unit->m_count = 0.f;
    }
    OUT0(0) = 0.f;
}


/////////////////////////////////////////////////////////////////////////////


void Dseries_next(Dseries* unit, int inNumSamples) {
    if (inNumSamples) {
        float step = DEMANDINPUT_A(2, inNumSamples);
        if (!sc_isnan(step)) {
            unit->m_step = step;
        }
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
            unit->m_value = DEMANDINPUT_A(1, inNumSamples);
        }
        if (unit->m_repeatCount >= unit->m_repeats) {
            OUT0(0) = NAN;
            return;
        }
        OUT0(0) = unit->m_value;
        unit->m_value += unit->m_step;
        unit->m_repeatCount++;
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
    }
}

void Dseries_Ctor(Dseries* unit) {
    SETCALC(Dseries_next);
    Dseries_next(unit, 0);
    OUT0(0) = 0.f;
}


void Dgeom_next(Dgeom* unit, int inNumSamples) {
    if (inNumSamples) {
        float grow = DEMANDINPUT_A(2, inNumSamples);
        if (!sc_isnan(grow)) {
            unit->m_grow = grow;
        }
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
            unit->m_value = DEMANDINPUT_A(1, inNumSamples);
        }
        if (unit->m_repeatCount >= unit->m_repeats) {
            OUT0(0) = NAN;
            return;
        }
        OUT0(0) = unit->m_value;
        unit->m_value *= unit->m_grow;
        unit->m_repeatCount++;
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
    }
}

void Dgeom_Ctor(Dgeom* unit) {
    SETCALC(Dgeom_next);
    Dgeom_next(unit, 0);
    OUT0(0) = 0.f;
}


void Dwhite_next(Dwhite* unit, int inNumSamples) {
    if (inNumSamples) {
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
        }
        if (unit->m_repeatCount >= unit->m_repeats) {
            OUT0(0) = NAN;
            return;
        }
        unit->m_repeatCount++;
        float lo = DEMANDINPUT_A(1, inNumSamples);
        float hi = DEMANDINPUT_A(2, inNumSamples);

        if (!sc_isnan(lo)) {
            unit->m_lo = lo;
        }
        if (!sc_isnan(hi)) {
            unit->m_range = hi - lo;
        }
        float x = unit->mParent->mRGen->frand() * unit->m_range + unit->m_lo;
        OUT0(0) = x;
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
    }
}

void Dwhite_Ctor(Dwhite* unit) {
    SETCALC(Dwhite_next);
    Dwhite_next(unit, 0);
    OUT0(0) = 0.f;
}


void Diwhite_next(Diwhite* unit, int inNumSamples) {
    if (inNumSamples) {
        float lo = DEMANDINPUT_A(1, inNumSamples);
        float hi = DEMANDINPUT_A(2, inNumSamples);

        if (!sc_isnan(lo)) {
            unit->m_lo = (int32)floor(DEMANDINPUT_A(1, inNumSamples) + 0.5f);
        }
        if (!sc_isnan(hi)) {
            int32 hi = (int32)floor(DEMANDINPUT_A(2, inNumSamples) + 0.5f);
            unit->m_range = hi - unit->m_lo + 1;
        }

        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
        }
        if (unit->m_repeatCount >= unit->m_repeats) {
            OUT0(0) = NAN;
            return;
        }
        unit->m_repeatCount++;
        float x = unit->mParent->mRGen->irand(unit->m_range) + unit->m_lo;
        OUT0(0) = x;
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
    }
}

void Diwhite_Ctor(Diwhite* unit) {
    SETCALC(Diwhite_next);
    Diwhite_next(unit, 0);
    OUT0(0) = 0.f;
}


void Dbrown_next(Dbrown* unit, int inNumSamples) {
    if (inNumSamples) {
        float lo = DEMANDINPUT_A(1, inNumSamples);
        if (!sc_isnan(lo)) {
            unit->m_lo = lo;
        }
        float hi = DEMANDINPUT_A(2, inNumSamples);
        if (!sc_isnan(hi)) {
            unit->m_hi = hi;
        }
        float step = DEMANDINPUT_A(3, inNumSamples);
        if (!sc_isnan(step)) {
            unit->m_step = step;
        }

        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
            unit->m_val = unit->mParent->mRGen->frand() * (unit->m_hi - unit->m_lo) + unit->m_lo;
        }
        if (unit->m_repeatCount >= unit->m_repeats) {
            OUT0(0) = NAN;
            return;
        }
        unit->m_repeatCount++;
        OUT0(0) = unit->m_val;
        float x = unit->m_val + unit->mParent->mRGen->frand2() * unit->m_step;
        unit->m_val = sc_fold(x, unit->m_lo, unit->m_hi);
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
    }
}

void Dbrown_Ctor(Dbrown* unit) {
    SETCALC(Dbrown_next);
    Dbrown_next(unit, 0);
    OUT0(0) = 0.f;
}


void Dibrown_next(Dibrown* unit, int inNumSamples) {
    if (inNumSamples) {
        float lo = DEMANDINPUT_A(1, inNumSamples);
        if (!sc_isnan(lo)) {
            unit->m_lo = (int32)lo;
        }
        float hi = DEMANDINPUT_A(2, inNumSamples);
        if (!sc_isnan(hi)) {
            unit->m_hi = (int32)hi;
        }
        float step = DEMANDINPUT_A(3, inNumSamples);
        if (!sc_isnan(step)) {
            unit->m_step = (int32)step;
        }

        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
            unit->m_val = unit->mParent->mRGen->irand(unit->m_hi - unit->m_lo + 1) + unit->m_lo;
        }
        if (unit->m_repeatCount >= unit->m_repeats) {
            OUT0(0) = NAN;
            return;
        }
        unit->m_repeatCount++;
        OUT0(0) = unit->m_val;
        int32 z = unit->m_val + unit->mParent->mRGen->irand2(unit->m_step);
        unit->m_val = sc_fold(z, unit->m_lo, unit->m_hi);
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
    }
}

void Dibrown_Ctor(Dibrown* unit) {
    SETCALC(Dibrown_next);
    Dibrown_next(unit, 0);
    OUT0(0) = 0.f;
}


void Dseq_next(Dseq* unit, int inNumSamples) {
    // Print("->Dseq_next %d\n", inNumSamples);
    if (inNumSamples) {
        // Print("   unit->m_repeats %d\n", unit->m_repeats);
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
        }
        int attempts = 0;
        while (true) {
            // Print("   unit->m_index %d   unit->m_repeatCount %d\n", unit->m_index, unit->m_repeatCount);
            if (unit->m_index >= unit->mNumInputs) {
                unit->m_index = 1;
                unit->m_repeatCount++;
            }
            if (unit->m_repeatCount >= unit->m_repeats) {
                // Print("done\n");
                OUT0(0) = NAN;
                unit->m_index = 1;
                return;
            }
            if (ISDEMANDINPUT(unit->m_index)) {
                if (unit->m_needToResetChild) {
                    unit->m_needToResetChild = false;
                    RESETINPUT(unit->m_index);
                }
                float x = DEMANDINPUT_A(unit->m_index, inNumSamples);
                if (sc_isnan(x)) {
                    unit->m_index++;
                    unit->m_needToResetChild = true;
                } else {
                    OUT0(0) = x;
                    return;
                }
            } else {
                OUT0(0) = DEMANDINPUT_A(unit->m_index, inNumSamples);
                // Print("   unit->m_index %d   OUT0(0) %g\n", unit->m_index, OUT0(0));
                unit->m_index++;
                unit->m_needToResetChild = true;
                return;
            }

            if (attempts++ > unit->mNumInputs) {
                Print("Warning: empty sequence in Dseq\n");
                return;
            }
        }
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
        unit->m_needToResetChild = true;
        unit->m_index = 1;
    }
}


void Dseq_Ctor(Dseq* unit) {
    SETCALC(Dseq_next);
    Dseq_next(unit, 0);
    OUT0(0) = 0.f;
}


void Dser_next(Dser* unit, int inNumSamples) {
    if (inNumSamples) {
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
        }
        while (true) {
            if (unit->m_index >= unit->mNumInputs) {
                unit->m_index = 1;
            }
            if (unit->m_repeatCount >= unit->m_repeats) {
                OUT0(0) = NAN;
                return;
            }
            if (ISDEMANDINPUT(unit->m_index)) {
                if (unit->m_needToResetChild) {
                    unit->m_needToResetChild = false;
                    RESETINPUT(unit->m_index);
                }
                float x = DEMANDINPUT_A(unit->m_index, inNumSamples);
                if (sc_isnan(x)) {
                    unit->m_index++;
                    unit->m_repeatCount++;
                    unit->m_needToResetChild = true;
                } else {
                    OUT0(0) = x;
                    return;
                }
            } else {
                OUT0(0) = IN0(unit->m_index);
                unit->m_index++;
                unit->m_repeatCount++;
                unit->m_needToResetChild = true;
                return;
            }
        }
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
        unit->m_needToResetChild = true;
        unit->m_index = 1;
    }
}

void Dser_Ctor(Dser* unit) {
    SETCALC(Dser_next);
    Dser_next(unit, 0);
    OUT0(0) = 0.f;
}


void Drand_next(Drand* unit, int inNumSamples) {
    if (inNumSamples) {
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
        }
        while (true) {
            if (unit->m_repeatCount >= unit->m_repeats) {
                OUT0(0) = NAN;
                return;
            }
            if (ISDEMANDINPUT(unit->m_index)) {
                if (unit->m_needToResetChild) {
                    unit->m_needToResetChild = false;
                    RESETINPUT(unit->m_index);
                }
                float x = DEMANDINPUT_A(unit->m_index, inNumSamples);
                if (sc_isnan(x)) {
                    unit->m_index = unit->mParent->mRGen->irand(unit->mNumInputs - 1) + 1;
                    unit->m_repeatCount++;
                    unit->m_needToResetChild = true;
                } else {
                    OUT0(0) = x;
                    return;
                }
            } else {
                OUT0(0) = DEMANDINPUT_A(unit->m_index, inNumSamples);
                unit->m_index = unit->mParent->mRGen->irand(unit->mNumInputs - 1) + 1;
                unit->m_repeatCount++;
                unit->m_needToResetChild = true;
                return;
            }
        }
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
        unit->m_needToResetChild = true;
        unit->m_index = unit->mParent->mRGen->irand(unit->mNumInputs - 1) + 1;
    }
}

void Drand_Ctor(Drand* unit) {
    SETCALC(Drand_next);
    Drand_next(unit, 0);
    OUT0(0) = 0.f;
}


void Dxrand_next(Dxrand* unit, int inNumSamples) {
    if (inNumSamples) {
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
        }
        while (true) {
            if (unit->m_index >= unit->mNumInputs) {
                unit->m_index = 1;
            }
            if (unit->m_repeatCount >= unit->m_repeats) {
                OUT0(0) = NAN;
                return;
            }
            if (ISDEMANDINPUT(unit->m_index)) {
                if (unit->m_needToResetChild) {
                    unit->m_needToResetChild = false;
                    RESETINPUT(unit->m_index);
                }
                float x = DEMANDINPUT_A(unit->m_index, inNumSamples);
                if (sc_isnan(x)) {
                    int newindex = unit->mParent->mRGen->irand(unit->mNumInputs - 2) + 1;
                    unit->m_index = newindex < unit->m_index ? newindex : newindex + 1;
                    unit->m_repeatCount++;
                    unit->m_needToResetChild = true;
                } else {
                    OUT0(0) = x;
                    return;
                }
            } else {
                OUT0(0) = DEMANDINPUT_A(unit->m_index, inNumSamples);
                int newindex = unit->mParent->mRGen->irand(unit->mNumInputs - 2) + 1;
                unit->m_index = newindex < unit->m_index ? newindex : newindex + 1;
                unit->m_repeatCount++;
                unit->m_needToResetChild = true;
                return;
            }
        }
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
        unit->m_needToResetChild = true;
        int newindex = unit->mParent->mRGen->irand(unit->mNumInputs - 2) + 1;
        unit->m_index = newindex < unit->m_index ? newindex : newindex + 1;
    }
}

void Dxrand_Ctor(Dxrand* unit) {
    SETCALC(Dxrand_next);
    Dxrand_next(unit, 0);
    OUT0(0) = 0.f;
}

#define WINDEX                                                                                                         \
    float w, sum = 0.0;                                                                                                \
    float r = unit->mParent->mRGen->frand();                                                                           \
    for (int i = 0; i < weights_size; ++i) {                                                                           \
        w = IN0(2 + i);                                                                                                \
        sum += w;                                                                                                      \
        if (sum >= r) {                                                                                                \
            unit->m_index = i + offset;                                                                                \
            break;                                                                                                     \
        }                                                                                                              \
    }


void Dwrand_next(Dwrand* unit, int inNumSamples) {
    int offset = unit->m_weights_size + 2;
    int weights_size = unit->mNumInputs - offset;
    if (inNumSamples) {
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
        }
        while (true) {
            if (unit->m_repeatCount >= unit->m_repeats) {
                OUT0(0) = NAN;
                return;
            }

            if (ISDEMANDINPUT(unit->m_index)) {
                if (unit->m_needToResetChild) {
                    unit->m_needToResetChild = false;
                    RESETINPUT(unit->m_index);
                }
                float x = DEMANDINPUT_A(unit->m_index, inNumSamples);
                if (sc_isnan(x)) {
                    WINDEX;
                    unit->m_repeatCount++;
                    unit->m_needToResetChild = true;
                } else {
                    OUT0(0) = x;
                    return;
                }
            } else {
                OUT0(0) = DEMANDINPUT_A(unit->m_index, inNumSamples);
                WINDEX;
                unit->m_repeatCount++;
                unit->m_needToResetChild = true;
                return;
            }
        }
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
        unit->m_needToResetChild = true;
        WINDEX;
    }
}

void Dwrand_Ctor(Dwrand* unit) {
    SETCALC(Dwrand_next);
    unit->m_weights_size = IN0(1);
    Dwrand_next(unit, 0);
    OUT0(0) = 0.f;
}


static void Dshuf_scramble(Dshuf* unit);

void Dshuf_next(Dshuf* unit, int inNumSamples) {
    // Print("->Dshuf_next %d\n", inNumSamples);
    if (inNumSamples) {
        // Print("   unit->m_repeats %d\n", unit->m_repeats);
        if (unit->m_repeats < 0.) {
            float x = DEMANDINPUT_A(0, inNumSamples);
            unit->m_repeats = sc_isnan(x) ? 0.f : floor(x + 0.5f);
        }
        while (true) {
            // Print("   unit->m_index %d   unit->m_repeatCount %d\n", unit->m_index, unit->m_repeatCount);
            if (unit->m_index >= (unit->mNumInputs - 1)) {
                unit->m_index = 0;
                unit->m_repeatCount++;
            }
            if (unit->m_repeatCount >= unit->m_repeats) {
                // Print("done\n");
                OUT0(0) = NAN;
                unit->m_index = 0;
                return;
            }
            if (ISDEMANDINPUT(unit->m_indices[unit->m_index])) {
                if (unit->m_needToResetChild) {
                    unit->m_needToResetChild = false;
                    RESETINPUT(unit->m_indices[unit->m_index]);
                }
                float x = DEMANDINPUT_A(unit->m_indices[unit->m_index], inNumSamples);

                if (sc_isnan(x)) {
                    unit->m_index++;
                    unit->m_needToResetChild = true;
                } else {
                    OUT0(0) = x;
                    return;
                }
            } else {
                OUT0(0) = DEMANDINPUT_A(unit->m_indices[unit->m_index], inNumSamples);
                // Print("   unit->m_index %d   OUT0(0) %g\n", unit->m_index, OUT0(0));
                unit->m_index++;
                unit->m_needToResetChild = true;
                return;
            }
        }
    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0;
        unit->m_needToResetChild = true;
        unit->m_index = 0;
        Dshuf_scramble(unit);
    }
}

void Dshuf_scramble(Dshuf* unit) {
    int32 i, j, m, k, size;
    int32 temp;

    size = (int32)(unit->mNumInputs) - 1;

    if (size > 1) {
        k = size;
        for (i = 0, m = k; i < k - 1; ++i, --m) {
            j = i + unit->mParent->mRGen->irand(m);
            temp = unit->m_indices[i];
            unit->m_indices[i] = unit->m_indices[j];
            unit->m_indices[j] = temp;
        }
    }
}

void Dshuf_Ctor(Dshuf* unit) {
    OUT0(0) = 0.f;

    uint32 size = (unit->mNumInputs) - 1;
    unit->m_indices = (int32*)RTAlloc(unit->mWorld, size * sizeof(int32));
    ClearUnitIfMemFailed(unit->m_indices);

    for (uint32 i = 0; i < size; ++i)
        unit->m_indices[i] = i + 1;

    SETCALC(Dshuf_next);
    Dshuf_next(unit, 0);
}

void Dshuf_Dtor(Dshuf* unit) { RTFree(unit->mWorld, unit->m_indices); }


void Dswitch1_next(Dswitch1* unit, int inNumSamples) {
    if (inNumSamples) {
        float x = DEMANDINPUT_A(0, inNumSamples);
        if (sc_isnan(x)) {
            OUT0(0) = x;
            return;
        }
        int index = (int32)floor(x + 0.5f);
        index = sc_wrap(index, 0, unit->mNumInputs - 2) + 1;
        OUT0(0) = DEMANDINPUT_A(index, inNumSamples);
    } else {
        for (int i = 0; i < unit->mNumInputs; ++i) {
            RESETINPUT(i);
        }
    }
}

void Dswitch1_Ctor(Dswitch1* unit) {
    SETCALC(Dswitch1_next);
    OUT0(0) = 0.f;
}

/////////////////////////////

void Dswitch_next(Dswitch* unit, int inNumSamples) {
    if (inNumSamples) {
        float val = DEMANDINPUT_A(unit->m_index, inNumSamples);
        // printf("index: %i\n", (int) val);
        if (sc_isnan(val)) {
            float ival = DEMANDINPUT_A(0, inNumSamples);

            if (sc_isnan(ival))
                val = ival;
            else {
                int index = (int32)floor(ival + 0.5f);
                index = sc_wrap(index, 0, unit->mNumInputs - 2) + 1;
                val = DEMANDINPUT_A(index, inNumSamples);

                RESETINPUT(unit->m_index);
                // printf("resetting index: %i\n", unit->m_index);
                unit->m_index = index;
            }
        }
        OUT0(0) = val;

    } else {
        printf("...\n");
        for (int i = 0; i < unit->mNumInputs; ++i) {
            RESETINPUT(i);
        }
        int index = (int32)floor(DEMANDINPUT(0) + 0.5f);
        index = sc_wrap(index, 0, unit->mNumInputs - 1) + 1;
        unit->m_index = index;
    }
}

void Dswitch_Ctor(Dswitch* unit) {
    SETCALC(Dswitch_next);
    int index = (int32)floor(DEMANDINPUT(0) + 0.5f);
    index = sc_wrap(index, 0, unit->mNumInputs - 1) + 1;
    unit->m_index = index;
    OUT0(0) = 0.f;
}

//////////////////////////////


void Dstutter_next(Dstutter* unit, int inNumSamples) {
    if (inNumSamples) {
        if (unit->m_repeatCount >= unit->m_repeats) {
            float val = DEMANDINPUT_A(1, inNumSamples);
            float repeats = DEMANDINPUT_A(0, inNumSamples);

            if (sc_isnan(repeats) || sc_isnan(val)) {
                OUT0(0) = NAN;
                return;
            } else {
                unit->m_value = val;
                unit->m_repeats = floor(repeats + 0.5f);
                unit->m_repeatCount = 0.f;
            }
        }

        OUT0(0) = unit->m_value;
        unit->m_repeatCount++;

    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0.f;
        RESETINPUT(0);
        RESETINPUT(1);
    }
}

void Dstutter_Ctor(Dstutter* unit) {
    SETCALC(Dstutter_next);
    Dstutter_next(unit, 0);
    OUT0(0) = 0.f;
}

void Ddup_next(Ddup* unit, int inNumSamples) {
    if (inNumSamples) {
        if (unit->m_repeatCount >= unit->m_repeats) {
            float val = DEMANDINPUT_A(1, inNumSamples);
            float repeats = DEMANDINPUT_A(0, inNumSamples);

            if (sc_isnan(repeats) || sc_isnan(val)) {
                OUT0(0) = NAN;
                return;
            } else {
                unit->m_value = val;
                unit->m_repeats = floor(repeats + 0.5f);
                unit->m_repeatCount = 0.f;
            }
        }

        OUT0(0) = unit->m_value;
        unit->m_repeatCount++;

    } else {
        unit->m_repeats = -1.f;
        unit->m_repeatCount = 0.f;
        RESETINPUT(0);
        RESETINPUT(1);
    }
}

void Ddup_Ctor(Ddup* unit) {
    SETCALC(Ddup_next);
    Ddup_next(unit, 0);
    OUT0(0) = 0.f;
}

//////////////////////////////

void Dconst_next(Dconst* unit, int inNumSamples) {
    float total, tolerance;

    if (inNumSamples) {
        if (sc_isnan(unit->m_runningsum)) {
            OUT0(0) = NAN;
            return;
        }

        if (unit->m_total < 0.f) {
            total = DEMANDINPUT_A(0, inNumSamples);
            tolerance = DEMANDINPUT_A(2, inNumSamples);
            if (sc_isnan(total) || sc_isnan(tolerance)) {
                OUT0(0) = NAN;
                return;
            }
            unit->m_total = total;
            unit->m_tolerance = tolerance;
        }
        {
            total = unit->m_total;
            tolerance = unit->m_tolerance;
        }

        float val = DEMANDINPUT_A(1, inNumSamples);

        if (sc_isnan(val)) {
            OUT0(0) = NAN;
            return;
        } else {
            float runningsum = unit->m_runningsum + val;
            if ((runningsum > total) || (fabs(total - runningsum) <= tolerance)) {
                OUT0(0) = total - unit->m_runningsum;
                unit->m_runningsum = NAN; // stop next time
            } else {
                unit->m_runningsum = runningsum;
                OUT0(0) = val;
            }
        }

    } else {
        unit->m_total = -1.f;
        unit->m_runningsum = 0.f;
        RESETINPUT(0);
        RESETINPUT(1);
        RESETINPUT(2);
    }
}

void Dconst_Ctor(Dconst* unit) {
    SETCALC(Dconst_next);
    Dconst_next(unit, 0);
    OUT0(0) = 0.f;
}

//////////////////////////////


void Dpoll_next(Dpoll* unit, int inNumSamples) {
    if (inNumSamples) {
        float x = DEMANDINPUT_A(0, inNumSamples);
        float run = DEMANDINPUT_A(2, inNumSamples) > 0.f;
        if (unit->m_mayprint && run) {
            Print("%s: %g block offset: %d\n", unit->m_id_string, x, inNumSamples - 1);
        }
        if (IN0(1) >= 0.0)
            SendTrigger(&unit->mParent->mNode, (int)IN0(1), x);
        OUT0(0) = x;
    } else {
        RESETINPUT(0);
    }
}

void Dpoll_Ctor(Dpoll* unit) {
    OUT0(0) = 0.f;

    const int idStringSize = (int)IN0(3);

    unit->m_id_string = (char*)RTAlloc(unit->mWorld, (idStringSize + 1) * sizeof(char));
    ClearUnitIfMemFailed(unit->m_id_string);

    for (int i = 0; i < idStringSize; i++)
        unit->m_id_string[i] = (char)IN0(4 + i);

    SETCALC(Dpoll_next);
    unit->m_id_string[idStringSize] = '\0';
    unit->m_mayprint = unit->mWorld->mVerbosity >= -1;
    OUT0(0) = 0.f;
}

void Dpoll_Dtor(Dpoll* unit) { RTFree(unit->mWorld, unit->m_id_string); }

//////////////////////////////


void Dreset_next(Dreset* unit, int inNumSamples) {
    if (inNumSamples) {
        float x = DEMANDINPUT_A(0, inNumSamples);
        float reset = DEMANDINPUT_A(1, inNumSamples);
        if (sc_isnan(x)) {
            OUT0(0) = NAN;
            return;
        }
        if (reset > 0.0 && (unit->prev_reset <= 0.0)) {
            RESETINPUT(0);
        }
        unit->prev_reset = reset;
        OUT0(0) = x;
    } else {
        RESETINPUT(0);
    }
}

void Dreset_Ctor(Dreset* unit) {
    SETCALC(Dreset_next);
    unit->prev_reset = 0.0;
    Dreset_next(unit, 0);
}


//////////////////////////////

inline double sc_loop(Unit* unit, double in, double hi, int loop) {
    // avoid the divide if possible
    if (in >= hi) {
        if (!loop) {
            unit->mDone = true;
            return hi;
        }
        in -= hi;
        if (in < hi)
            return in;
    } else if (in < 0.) {
        if (!loop) {
            unit->mDone = true;
            return 0.;
        }
        in += hi;
        if (in >= 0.)
            return in;
    } else
        return in;

    return in - hi * floor(in / hi);
}

#define D_CHECK_BUF                                                                                                    \
    if (!bufData) {                                                                                                    \
        unit->mDone = true;                                                                                            \
        ClearUnitOutputs(unit, 1);                                                                                     \
        return;                                                                                                        \
    }

#define D_GET_BUF                                                                                                      \
    float fbufnum = DEMANDINPUT_A(0, inNumSamples);                                                                    \
    ;                                                                                                                  \
    if (sc_isnan(fbufnum)) {                                                                                           \
        OUT0(0) = NAN;                                                                                                 \
        return;                                                                                                        \
    }                                                                                                                  \
    fbufnum = sc_max(0.f, fbufnum);                                                                                    \
    if (fbufnum != unit->m_fbufnum) {                                                                                  \
        int bufnum = (int)fbufnum;                                                                                     \
        World* world = unit->mWorld;                                                                                   \
        if (bufnum < 0) {                                                                                              \
            bufnum = 0;                                                                                                \
        }                                                                                                              \
        if (bufnum >= world->mNumSndBufs) {                                                                            \
            int localBufNum = bufnum - world->mNumSndBufs;                                                             \
            Graph* parent = unit->mParent;                                                                             \
            if (localBufNum <= parent->localBufNum) {                                                                  \
                unit->m_buf = parent->mLocalSndBufs + localBufNum;                                                     \
            } else {                                                                                                   \
                bufnum = 0;                                                                                            \
                unit->m_buf = world->mSndBufs + bufnum;                                                                \
            }                                                                                                          \
        } else {                                                                                                       \
            unit->m_buf = world->mSndBufs + bufnum;                                                                    \
        }                                                                                                              \
        unit->m_fbufnum = fbufnum;                                                                                     \
    }                                                                                                                  \
    SndBuf* buf = unit->m_buf;                                                                                         \
    LOCK_SNDBUF(buf);                                                                                                  \
    float* bufData __attribute__((__unused__)) = buf->data;                                                            \
    uint32 bufChannels __attribute__((__unused__)) = buf->channels;                                                    \
    uint32 bufSamples __attribute__((__unused__)) = buf->samples;                                                      \
    uint32 bufFrames = buf->frames;                                                                                    \
    int mask __attribute__((__unused__)) = buf->mask;                                                                  \
    int guardFrame __attribute__((__unused__)) = bufFrames - 2;

#define D_GET_BUF_SHARED                                                                                               \
    float fbufnum = DEMANDINPUT_A(0, inNumSamples);                                                                    \
    ;                                                                                                                  \
    if (sc_isnan(fbufnum)) {                                                                                           \
        OUT0(0) = NAN;                                                                                                 \
        return;                                                                                                        \
    }                                                                                                                  \
    fbufnum = sc_max(0.f, fbufnum);                                                                                    \
    if (fbufnum != unit->m_fbufnum) {                                                                                  \
        int bufnum = (int)fbufnum;                                                                                     \
        World* world = unit->mWorld;                                                                                   \
        if (bufnum < 0) {                                                                                              \
            bufnum = 0;                                                                                                \
        }                                                                                                              \
        if (bufnum >= world->mNumSndBufs) {                                                                            \
            int localBufNum = bufnum - world->mNumSndBufs;                                                             \
            Graph* parent = unit->mParent;                                                                             \
            if (localBufNum <= parent->localBufNum) {                                                                  \
                unit->m_buf = parent->mLocalSndBufs + localBufNum;                                                     \
            } else {                                                                                                   \
                bufnum = 0;                                                                                            \
                unit->m_buf = world->mSndBufs + bufnum;                                                                \
            }                                                                                                          \
        } else {                                                                                                       \
            unit->m_buf = world->mSndBufs + bufnum;                                                                    \
        }                                                                                                              \
        unit->m_fbufnum = fbufnum;                                                                                     \
    }                                                                                                                  \
    const SndBuf* buf = unit->m_buf;                                                                                   \
    LOCK_SNDBUF_SHARED(buf);                                                                                           \
    const float* bufData __attribute__((__unused__)) = buf->data;                                                      \
    uint32 bufChannels __attribute__((__unused__)) = buf->channels;                                                    \
    uint32 bufSamples __attribute__((__unused__)) = buf->samples;                                                      \
    uint32 bufFrames = buf->frames;                                                                                    \
    int mask __attribute__((__unused__)) = buf->mask;                                                                  \
    int guardFrame __attribute__((__unused__)) = bufFrames - 2;


void Dbufrd_next(Dbufrd* unit, int inNumSamples) {
    int32 loop = (int32)DEMANDINPUT_A(2, inNumSamples);

    D_GET_BUF_SHARED
    D_CHECK_BUF

    double loopMax = (double)(loop ? bufFrames : bufFrames - 1);

    double phase;
    if (inNumSamples) {
        float x = DEMANDINPUT_A(1, inNumSamples);
        if (sc_isnan(x)) {
            OUT0(0) = NAN;
            return;
        }
        phase = x;

        phase = sc_loop((Unit*)unit, phase, loopMax, loop);
        int32 iphase = (int32)phase;
        const float* table1 = bufData + iphase * bufChannels;
        OUT0(0) = table1[0];
    } else {
        RESETINPUT(1);
    }
}

void Dbufrd_Ctor(Dbufrd* unit) {
    SETCALC(Dbufrd_next);

    unit->m_fbufnum = -1e9f;

    Dbufrd_next(unit, 0);
    OUT0(0) = 0.f;
}

////////////////////////////////////

void Dbufwr_next(Dbufwr* unit, int inNumSamples) {
    int32 loop = (int32)DEMANDINPUT_A(3, inNumSamples);

    D_GET_BUF
    D_CHECK_BUF

    double loopMax = (double)(loop ? bufFrames : bufFrames - 1);

    double phase;
    float val;
    if (inNumSamples) {
        float x = DEMANDINPUT_A(1, inNumSamples);
        if (sc_isnan(x)) {
            OUT0(0) = NAN;
            return;
        }
        phase = x;
        val = DEMANDINPUT_A(2, inNumSamples);
        if (sc_isnan(val)) {
            OUT0(0) = NAN;
            return;
        }

        phase = sc_loop((Unit*)unit, phase, loopMax, loop);
        int32 iphase = (int32)phase;
        float* table0 = bufData + iphase * bufChannels;
        table0[0] = val;
        OUT0(0) = val;
    } else {
        RESETINPUT(1);
        RESETINPUT(2);
    }
}

void Dbufwr_Ctor(Dbufwr* unit) {
    SETCALC(Dbufwr_next);

    unit->m_fbufnum = -1e9f;

    Dbufwr_next(unit, 0);
    OUT0(0) = 0.f;
}


//////////////////////////////////////////////////////


PluginLoad(Demand) {
    ft = inTable;

    DefineDtorCantAliasUnit(Demand);
    DefineSimpleCantAliasUnit(Duty);
    DefineSimpleCantAliasUnit(DemandEnvGen);
    DefineSimpleCantAliasUnit(TDuty);
    DefineSimpleUnit(Dseries);
    DefineSimpleUnit(Dgeom);
    DefineSimpleUnit(Dwhite);
    DefineSimpleUnit(Dbrown);
    DefineSimpleUnit(Diwhite);
    DefineSimpleUnit(Dibrown);
    DefineSimpleUnit(Dseq);
    DefineSimpleUnit(Dser);
    DefineSimpleUnit(Dbufrd);
    DefineSimpleUnit(Dbufwr);
    DefineSimpleUnit(Drand);
    DefineSimpleUnit(Dwrand);
    DefineSimpleUnit(Dxrand);
    DefineDtorUnit(Dshuf);
    DefineSimpleUnit(Dswitch1);
    DefineSimpleUnit(Dswitch);
    DefineSimpleUnit(Dstutter);
    DefineSimpleUnit(Ddup);
    DefineSimpleUnit(Dconst);
    DefineSimpleUnit(Dreset);
    DefineDtorUnit(Dpoll);
}
