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

static InterfaceTable* ft;

struct WhiteNoise : public Unit {};

struct ClipNoise : public Unit {};

struct BrownNoise : public Unit {
    float mLevel;
};

struct PinkNoise : public Unit {
    uint32 mDice[16];
    int32 mTotal;
};

struct Dust : public Unit {
    float m_density, m_thresh, m_scale;
};

struct Dust2 : public Unit {
    float m_density, m_thresh, m_scale;
};

struct GrayNoise : public Unit {
    int32 mCounter;
};

struct Crackle : public Unit {
    double m_y1, m_y2;
};

struct Logistic : public Unit {
    double m_y1;
    int mCounter;
};

struct Hasher : public Unit {};

struct MantissaMask : public Unit {};

struct IRand : public Unit {};

struct Rand : public Unit {};

struct TRand : public Unit {
    float m_trig, m_value;
};

struct TIRand : public Unit {
    float m_trig, m_value;
};

struct TExpRand : public Unit {
    float m_trig, m_value;
};

struct NRand : public Unit {};

struct LinRand : public Unit {};

struct ExpRand : public Unit {};

struct CoinGate : public Unit {
    float m_trig;
};

struct LFClipNoise : public Unit {
    float mLevel;
    int mCounter;
};

struct LFNoise0 : public Unit {
    float mLevel;
    int mCounter;
};

struct LFNoise1 : public Unit {
    float mLevel, mSlope;
    int mCounter;
};

struct LFNoise2 : public Unit {
    float mLevel, mSlope, mCurve;
    float m_nextvalue, m_nextmidpt;
    int mCounter;
};

struct RandSeed : public Unit {
    float m_trig;
};

struct RandID : public Unit {
    float m_id;
};

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


extern "C" {
void WhiteNoise_next(WhiteNoise* unit, int inNumSamples);
void WhiteNoise_Ctor(WhiteNoise* unit);

void GrayNoise_next(GrayNoise* unit, int inNumSamples);
void GrayNoise_Ctor(GrayNoise* unit);

void ClipNoise_next(ClipNoise* unit, int inNumSamples);
void ClipNoise_Ctor(ClipNoise* unit);

void PinkNoise_next(PinkNoise* unit, int inNumSamples);
void PinkNoise_Ctor(PinkNoise* unit);

void BrownNoise_next(BrownNoise* unit, int inNumSamples);
void BrownNoise_Ctor(BrownNoise* unit);

void Dust_next(Dust* unit, int inNumSamples);
void Dust_Ctor(Dust* unit);

void Dust2_next(Dust2* unit, int inNumSamples);
void Dust2_Ctor(Dust2* unit);

void Crackle_next(Crackle* unit, int inNumSamples);
void Crackle_Ctor(Crackle* unit);

void Hasher_next(Hasher* unit, int inNumSamples);
void Hasher_Ctor(Hasher* unit);

void MantissaMask_next(MantissaMask* unit, int inNumSamples);
void MantissaMask_Ctor(MantissaMask* unit);

void IRand_Ctor(IRand* unit);
void Rand_Ctor(Rand* unit);
void LinRand_Ctor(LinRand* unit);
void NRand_Ctor(NRand* unit);
void ExpRand_Ctor(ExpRand* unit);

void CoinGate_Ctor(CoinGate* unit);
void CoinGate_next_k(CoinGate* unit, int inNumSamples);
void CoinGate_next(CoinGate* unit, int inNumSamples);

void TIRand_next_a(TIRand* unit, int inNumSamples);
void TIRand_next_k(TIRand* unit, int inNumSamples);
void TIRand_Ctor(TIRand* unit);

void TRand_next_a(TRand* unit, int inNumSamples);
void TRand_next_k(TRand* unit, int inNumSamples);
void TRand_Ctor(TRand* unit);

void TExpRand_next_a(TExpRand* unit, int inNumSamples);
void TExpRand_next_k(TExpRand* unit, int inNumSamples);
void TExpRand_Ctor(TExpRand* unit);

void Logistic_next_1(Logistic* unit, int inNumSamples);
void Logistic_next_k(Logistic* unit, int inNumSamples);
void Logistic_Ctor(Logistic* unit);

void LFClipNoise_next(LFClipNoise* unit, int inNumSamples);
void LFClipNoise_Ctor(LFClipNoise* unit);

void LFNoise0_next(LFNoise0* unit, int inNumSamples);
void LFNoise0_Ctor(LFNoise0* unit);

void LFNoise1_next(LFNoise1* unit, int inNumSamples);
void LFNoise1_Ctor(LFNoise1* unit);

void LFNoise2_next(LFNoise2* unit, int inNumSamples);
void LFNoise2_Ctor(LFNoise2* unit);

void RandSeed_next(RandSeed* unit, int inNumSamples);
void RandSeed_next_k(RandSeed* unit, int inNumSamples);
void RandSeed_Ctor(RandSeed* unit);

void RandID_next(RandID* unit, int inNumSamples);
void RandID_Ctor(RandID* unit);
}

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

void ClipNoise_next(ClipNoise* unit, int inNumSamples) {
    float* out = ZOUT(0);

    RGET LOOP1(inNumSamples, ZXP(out) = fcoin(s1, s2, s3););
    RPUT
}

void ClipNoise_Ctor(ClipNoise* unit) {
    SETCALC(ClipNoise_next);

    ClipNoise_next(unit, 1);
}

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

void GrayNoise_next(GrayNoise* unit, int inNumSamples) {
    float* out = ZOUT(0);

    RGET int counter = unit->mCounter;

    LOOP1(inNumSamples, counter ^= 1L << (trand(s1, s2, s3) & 31); ZXP(out) = counter * 4.65661287308e-10f;);
    unit->mCounter = counter;
    RPUT
}

void GrayNoise_Ctor(GrayNoise* unit) {
    SETCALC(GrayNoise_next);
    unit->mCounter = 0;

    GrayNoise_next(unit, 1);
}

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

void WhiteNoise_next(WhiteNoise* unit, int inNumSamples) {
    float* out = ZOUT(0);

    RGET LOOP1(inNumSamples, ZXP(out) = frand2(s1, s2, s3););
    RPUT
}

void WhiteNoise_Ctor(WhiteNoise* unit) {
    SETCALC(WhiteNoise_next);

    WhiteNoise_next(unit, 1);
}


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

void PinkNoise_next(PinkNoise* unit, int inNumSamples) {
    float* out = ZOUT(0);

    RGET

        uint32 total = unit->mTotal;
    uint32* dice = unit->mDice;
    LOOP1(inNumSamples, uint32 counter = trand(s1, s2, s3); // Magnus Jonsson's suggestion.
          uint32 newrand = counter >> 13; int k = (CTZ(counter)) & 15; uint32 prevrand = dice[k]; dice[k] = newrand;
          total += (newrand - prevrand); newrand = trand(s1, s2, s3) >> 13; elem32 val; // ensure write before read <sk>
          val.u = (total + newrand) | 0x40000000; ZXP(out) = val.f - 3.0f; counter++;);
    unit->mTotal = total;
    RPUT
}

void PinkNoise_Ctor(PinkNoise* unit) {
    SETCALC(PinkNoise_next);

    RGET uint32* dice = unit->mDice;
    int32 total = 0;
    for (int i = 0; i < 16; ++i) {
        uint32 newrand = trand(s1, s2, s3) >> 13;
        total += newrand;
        dice[i] = newrand;
    }
    unit->mTotal = total;

    RPUT

        PinkNoise_next(unit, 1);
}

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

void BrownNoise_next(BrownNoise* unit, int inNumSamples) {
    float* out = ZOUT(0);
    RGET

        float z = unit->mLevel;
    LOOP1(inNumSamples, z += frand8(s1, s2, s3); if (z > 1.f) z = 2.f - z; else if (z < -1.f) z = -2.f - z;
          ZXP(out) = z;);
    unit->mLevel = z;
    RPUT
}

void BrownNoise_Ctor(BrownNoise* unit) {
    SETCALC(BrownNoise_next);

    unit->mLevel = unit->mParent->mRGen->frand2();

    ZOUT0(0) = unit->mLevel;
}

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

void Dust_Ctor(Dust* unit) {
    SETCALC(Dust_next);

    unit->m_density = 0.f;
    unit->m_scale = 0.f;
    unit->m_thresh = 0.f;
    Dust_next(unit, 1);
}

void Dust_next(Dust* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float density = ZIN0(0);
    float thresh, scale;

    RGET

        if (density != unit->m_density) {
        thresh = unit->m_thresh = density * unit->mRate->mSampleDur;
        scale = unit->m_scale = thresh > 0.f ? 1.f / thresh : 0.f;
        unit->m_density = density;
    }
    else {
        thresh = unit->m_thresh;
        scale = unit->m_scale;
    }

    LOOP1(inNumSamples, float z = frand(s1, s2, s3); if (z < thresh) ZXP(out) = z * scale; else ZXP(out) = 0.f;);

    RPUT
}

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

void Dust2_Ctor(Dust2* unit) {
    SETCALC(Dust2_next);

    unit->m_density = 0.f;
    unit->m_scale = 0.f;
    unit->m_thresh = 0.f;
    Dust2_next(unit, 1);
}

void Dust2_next(Dust2* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float density = ZIN0(0);
    float thresh, scale;

    RGET

        if (density != unit->m_density) {
        thresh = unit->m_thresh = density * unit->mRate->mSampleDur;
        scale = unit->m_scale = thresh > 0.f ? 2.f / thresh : 0.f;
        unit->m_density = density;
    }
    else {
        thresh = unit->m_thresh;
        scale = unit->m_scale;
    }

    LOOP1(inNumSamples, float z = frand(s1, s2, s3); if (z < thresh) ZXP(out) = z * scale - 1.f; else ZXP(out) = 0.f;);

    RPUT
}

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

void Crackle_next(Crackle* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float paramf = ZIN0(0);
    float y1 = unit->m_y1;
    float y2 = unit->m_y2;
    float y0;

    LOOP1(inNumSamples, ZXP(out) = y0 = fabs(y1 * paramf - y2 - 0.05f); y2 = y1; y1 = y0;);
    unit->m_y1 = y1;
    unit->m_y2 = y2;
}

void Crackle_Ctor(Crackle* unit) {
    SETCALC(Crackle_next);

    unit->m_y1 = unit->mParent->mRGen->drand();
    unit->m_y2 = 0.f;

    Crackle_next(unit, 1);
}

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

void Logistic_next_1(Logistic* unit, int inNumSamples) {
    float* out = ZOUT(0);
    double paramf = ZIN0(0);
    double y1 = unit->m_y1;

    LOOP1(inNumSamples,
          ZXP(out) = y1 = paramf * y1 * (1.0 - y1); // chaotic equation
    );
    unit->m_y1 = y1;
}

void Logistic_next_k(Logistic* unit, int inNumSamples) {
    float* out = ZOUT(0);
    double paramf = ZIN0(0);
    float freq = ZIN0(1);
    double y1 = unit->m_y1;
    int32 counter = unit->mCounter;

    long remain = inNumSamples;
    do {
        if (counter <= 0) {
            counter = (int32)(unit->mRate->mSampleRate / sc_max(freq, .001f));
            counter = sc_max(1, counter);
            y1 = paramf * y1 * (1.0 - y1); // chaotic equation
        }
        long nsmps = sc_min(counter, remain);
        counter -= nsmps;
        remain -= nsmps;
        LOOP(nsmps, ZXP(out) = y1;);
    } while (remain);
    unit->m_y1 = y1;
    unit->mCounter = counter;
}

void Logistic_Ctor(Logistic* unit) {
    if (INRATE(0) == calc_ScalarRate && ZIN0(1) >= unit->mRate->mSampleRate)
        SETCALC(Logistic_next_1);
    else
        SETCALC(Logistic_next_k);

    unit->m_y1 = ZIN0(2);
    unit->mCounter = 0;

    Logistic_next_1(unit, 1);
}

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

void Rand_Ctor(Rand* unit) {
    float lo = ZIN0(0);
    float hi = ZIN0(1);
    float range = hi - lo;
    RGen& rgen = *unit->mParent->mRGen;
    ZOUT0(0) = rgen.frand() * range + lo;
}

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

void TRand_next_k(TRand* unit, int inNumSamples) {
    float trig = ZIN0(2);
    if (trig > 0.f && unit->m_trig <= 0.f) {
        float lo = ZIN0(0);
        float hi = ZIN0(1);
        float range = hi - lo;
        RGen& rgen = *unit->mParent->mRGen;
        ZOUT0(0) = unit->m_value = rgen.frand() * range + lo;
    } else {
        ZOUT0(0) = unit->m_value;
    }
    unit->m_trig = trig;
}

void TRand_next_a(TRand* unit, int inNumSamples) {
    float lo = ZIN0(0);
    float hi = ZIN0(1);
    float* trig = ZIN(2);
    float* out = ZOUT(0);
    float prev = unit->m_trig;
    float outval = unit->m_value;
    float next;

    LOOP1(
        inNumSamples, next = ZXP(trig); if (next > 0.f && prev <= 0.f) {
            float range = hi - lo;
            RGen& rgen = *unit->mParent->mRGen;
            ZXP(out) = outval = rgen.frand() * range + lo;
        } else { ZXP(out) = outval; };
        prev = next;)

    unit->m_trig = next;
    unit->m_value = outval;
}

void TRand_next_aa(TRand* unit, int inNumSamples) {
    float* lo = ZIN(0);
    float* hi = ZIN(1);
    float* trig = ZIN(2);
    float* out = ZOUT(0);
    float prev = unit->m_trig;
    float outval = unit->m_value;
    float next;

    LOOP1(
        inNumSamples, next = ZXP(trig); float loval = ZXP(lo); float hival = ZXP(hi); if (next > 0.f && prev <= 0.f) {
            RGen& rgen = *unit->mParent->mRGen;
            float range = hival - loval;
            ZXP(out) = outval = rgen.frand() * range + loval;
        } else { ZXP(out) = outval; };
        prev = next;)

    unit->m_trig = next;
    unit->m_value = outval;
}

void TRand_Ctor(TRand* unit) {
    float lo = ZIN0(0);
    float hi = ZIN0(1);
    float range = hi - lo;
    RGen& rgen = *unit->mParent->mRGen;
    ZOUT0(0) = unit->m_value = rgen.frand() * range + lo;
    if (unit->mCalcRate == calc_FullRate) {
        if (INRATE(0) == calc_FullRate) {
            SETCALC(TRand_next_aa);
        } else {
            SETCALC(TRand_next_a);
        }
    } else {
        SETCALC(TRand_next_k);
    }
    unit->m_trig = ZIN0(2);
}


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

void TExpRand_next_k(TExpRand* unit, int inNumSamples) {
    float trig = ZIN0(2);
    if (trig > 0.f && unit->m_trig <= 0.f) {
        float lo = ZIN0(0);
        float hi = ZIN0(1);
        float ratio = hi / lo;
        RGen& rgen = *unit->mParent->mRGen;
        ZOUT0(0) = unit->m_value = pow(ratio, rgen.frand()) * lo;
    } else {
        ZOUT0(0) = unit->m_value;
    }
    unit->m_trig = trig;
}

void TExpRand_next_a(TExpRand* unit, int inNumSamples) {
    float lo = ZIN0(0);
    float hi = ZIN0(1);
    float* trig = ZIN(2);
    float* out = ZOUT(0);
    float prev = unit->m_trig;
    float outval = unit->m_value;
    float next;

    LOOP1(
        inNumSamples, next = ZXP(trig); if (next > 0.f && prev <= 0.f) {
            float ratio = hi / lo;
            RGen& rgen = *unit->mParent->mRGen;
            ZXP(out) = outval = pow(ratio, rgen.frand()) * lo;
        } else { ZXP(out) = outval; })

    unit->m_trig = next;
    unit->m_value = outval;
}

void TExpRand_next_aa(TExpRand* unit, int inNumSamples) {
    float* lo = ZIN(0);
    float* hi = ZIN(1);
    float* trig = ZIN(2);
    float* out = ZOUT(0);
    float prev = unit->m_trig;
    float outval = unit->m_value;
    float next;

    LOOP1(
        inNumSamples, next = ZXP(trig); float loval = ZXP(lo); float hival = ZXP(hi); if (next > 0.f && prev <= 0.f) {
            float ratio = hival / loval;
            RGen& rgen = *unit->mParent->mRGen;
            ZXP(out) = outval = pow(ratio, rgen.frand()) * loval;
        } else { ZXP(out) = outval; })

    unit->m_trig = next;
    unit->m_value = outval;
}

void TExpRand_Ctor(TExpRand* unit) {
    float lo = ZIN0(0);
    float hi = ZIN0(1);
    float ratio = hi / lo;
    RGen& rgen = *unit->mParent->mRGen;

    ZOUT0(0) = unit->m_value = pow(ratio, rgen.frand()) * lo;
    if (unit->mCalcRate == calc_FullRate) {
        if (INRATE(0) == calc_FullRate) {
            SETCALC(TExpRand_next_aa);
        } else {
            SETCALC(TExpRand_next_a);
        }
    } else {
        SETCALC(TExpRand_next_k);
    }
    unit->m_trig = ZIN0(2);
}


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

void IRand_Ctor(IRand* unit) {
    int lo = (int)ZIN0(0);
    int hi = (int)ZIN0(1);
    int range = hi - lo + 1;
    RGen& rgen = *unit->mParent->mRGen;
    ZOUT0(0) = (float)(rgen.irand(range) + lo);
}

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

void TIRand_next_k(TIRand* unit, int inNumSamples) {
    float trig = ZIN0(2);
    if (trig > 0.f && unit->m_trig <= 0.f) {
        int lo = (int)ZIN0(0);
        int hi = (int)ZIN0(1);
        int range = hi - lo + 1;
        RGen& rgen = *unit->mParent->mRGen;
        ZOUT0(0) = unit->m_value = (float)(rgen.irand(range) + lo);
    } else {
        ZOUT0(0) = unit->m_value;
    }
    unit->m_trig = trig;
}

void TIRand_next_a(TIRand* unit, int inNumSamples) {
    int lo = (int)ZIN0(0);
    int hi = (int)ZIN0(1);
    float* trig = ZIN(2);
    float* out = ZOUT(0);
    float prev = unit->m_trig;
    float outval = unit->m_value;
    float next;

    LOOP1(
        inNumSamples, next = ZXP(trig); if (next > 0.f && prev <= 0.f) {
            int range = hi - lo + 1;
            RGen& rgen = *unit->mParent->mRGen;
            ZXP(out) = outval = (float)(rgen.irand(range) + lo);
        } else { ZXP(out) = outval; })

    unit->m_trig = next;
    unit->m_value = outval;
}

void TIRand_next_aa(TIRand* unit, int inNumSamples) {
    float* lo = ZIN(0);
    float* hi = ZIN(1);
    float* trig = ZIN(2);
    float prev = unit->m_trig;
    float* out = ZOUT(0);
    float outval = unit->m_value;
    float next;

    LOOP1(
        inNumSamples, next = ZXP(trig); int loval = (int)ZXP(lo); int hival = (int)ZXP(hi);
        if (next > 0.f && prev <= 0.f) {
            int range = hival - loval + 1;
            RGen& rgen = *unit->mParent->mRGen;
            ZXP(out) = outval = (float)(rgen.irand(range) + loval);
        } else { ZXP(out) = outval; })

    unit->m_trig = next;
    unit->m_value = outval;
}


void TIRand_Ctor(TIRand* unit) {
    int lo = (int)ZIN0(0);
    int hi = (int)ZIN0(1);
    int range = hi - lo + 1;
    RGen& rgen = *unit->mParent->mRGen;
    ZOUT0(0) = unit->m_value = (float)(rgen.irand(range) + lo);
    if (unit->mCalcRate == calc_FullRate) {
        if (INRATE(0) == calc_FullRate) {
            SETCALC(TIRand_next_aa);
        } else {
            SETCALC(TIRand_next_a);
        }
    } else {
        SETCALC(TIRand_next_k);
    }
    unit->m_trig = ZIN0(2);
}


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

void CoinGate_Ctor(CoinGate* unit) {
    if (unit->mCalcRate == calc_FullRate) {
        SETCALC(CoinGate_next);
    } else {
        SETCALC(CoinGate_next_k);
    }
    unit->m_trig = ZIN0(1);
    ClearUnitOutputs(unit, 1);
}

void CoinGate_next_k(CoinGate* unit, int inNumSamples) {
    float trig = ZIN0(1);
    float level = 0.f;
    RGen& rgen = *unit->mParent->mRGen;
    if (trig > 0.f && unit->m_trig <= 0.f) {
        if (rgen.frand() < ZIN0(0)) {
            level = trig;
        }
    }

    ZOUT0(0) = level;
    unit->m_trig = trig;
}

void CoinGate_next(CoinGate* unit, int inNumSamples) {
    float* trig = ZIN(1);
    float* out = ZOUT(0);
    float prevtrig = unit->m_trig;
    float probability = ZIN0(0);
    RGen& rgen = *unit->mParent->mRGen;
    LOOP1(
        inNumSamples, float curtrig = ZXP(trig); float level = 0.f; if (prevtrig <= 0.f && curtrig > 0.f) {
            if (rgen.frand() < probability) {
                level = curtrig;
            } else {
                level = 0.f;
            }
        } prevtrig = curtrig;
        ZXP(out) = level;)

    unit->m_trig = prevtrig;
}

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

void RandSeed_Ctor(RandSeed* unit) {
    unit->m_trig = 0.;
    if (unit->mCalcRate == calc_FullRate) {
        SETCALC(RandSeed_next);
    } else {
        SETCALC(RandSeed_next_k);
    }
    RandSeed_next(unit, 1);
}

void RandSeed_next_k(RandSeed* unit, int inNumSamples) {
    float trig = ZIN0(0);

    if (trig > 0.f && unit->m_trig <= 0.f) {
        RGen& rgen = *unit->mParent->mRGen;
        int seed = (int)DEMANDINPUT_A(1, inNumSamples);
        rgen.init(seed);
    }
    unit->m_trig = trig;
    ZOUT0(0) = 0.f;
}

void RandSeed_next(RandSeed* unit, int inNumSamples) {
    float* trig = ZIN(0);
    float* out = ZOUT(0);

    float prevtrig = unit->m_trig;
    float curtrig;

    LOOP1(
        inNumSamples,

        curtrig = ZXP(trig);
        if (curtrig > 0.f && prevtrig <= 0.f) {
            RGen& rgen = *unit->mParent->mRGen;
            int seed = (int)DEMANDINPUT_A(1, inNumSamples);
            rgen.init(seed);
        } prevtrig = curtrig;
        ZXP(out) = 0.f;

    )

    unit->m_trig = curtrig;
}

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

void RandID_Ctor(RandID* unit) {
    unit->m_id = -1.;
    SETCALC(RandID_next);
    RandID_next(unit, 1);
}

void RandID_next(RandID* unit, int inNumSamples) {
    float id = ZIN0(0);

    if (id != unit->m_id) {
        unit->m_id = id;
        uint32 iid = (uint32)id;
        if (iid < unit->mWorld->mNumRGens) {
            unit->mParent->mRGen = unit->mWorld->mRGen + iid;
        }
    }
    ZOUT0(0) = 0.f;
}

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

void LinRand_Ctor(LinRand* unit) {
    float lo = ZIN0(0);
    float hi = ZIN0(1);
    int n = (int)ZIN0(2);

    float range = hi - lo;
    RGen& rgen = *unit->mParent->mRGen;
    float a, b;
    a = rgen.frand();
    b = rgen.frand();
    if (n <= 0) {
        ZOUT0(0) = sc_min(a, b) * range + lo;
    } else {
        ZOUT0(0) = sc_max(a, b) * range + lo;
    }
}

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

void NRand_Ctor(NRand* unit) {
    float lo = ZIN0(0);
    float hi = ZIN0(1);
    int n = (int)ZIN0(2);

    float range = hi - lo;
    RGen& rgen = *unit->mParent->mRGen;
    float sum = 0;
    for (int i = 0; i < n; ++i) {
        sum += rgen.frand();
    }
    ZOUT0(0) = (sum / n) * range + lo;
}

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

void ExpRand_Ctor(ExpRand* unit) {
    float lo = ZIN0(0);
    float hi = ZIN0(1);
    float ratio = hi / lo;

    ZOUT0(0) = pow(ratio, unit->mParent->mRGen->frand()) * lo;
}

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

void Hasher_next(Hasher* unit, int inNumSamples) {
    int32* in = (int32*)ZIN(0);
    float* out = ZOUT(0);

    LOOP1(
        inNumSamples,
        union {
            float f;
            int i;
        } u;
        int z = ZXP(in); u.i = 0x40000000 | ((uint32)Hash(z) >> 9); ZXP(out) = u.f - 3.f;);
}

void Hasher_Ctor(Hasher* unit) {
    SETCALC(Hasher_next);

    Hasher_next(unit, 1);
}

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

void MantissaMask_next(MantissaMask* unit, int inNumSamples) {
    int32* in = (int32*)ZIN(0);
    int32 bits = (int32)ZIN0(1);
    int32* out = (int32*)ZOUT(0);
    int32 mask = -1 << (23 - bits);
    LOOP1(inNumSamples, ZXP(out) = mask & ZXP(in););
}

void MantissaMask_Ctor(MantissaMask* unit) {
    SETCALC(MantissaMask_next);

    MantissaMask_next(unit, 1);
}

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

void LFClipNoise_next(LFClipNoise* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freq = ZIN0(0);
    float level = unit->mLevel;
    int32 counter = unit->mCounter;
    RGET

        int remain = inNumSamples;
    do {
        if (counter <= 0) {
            counter = (int)(unit->mRate->mSampleRate / sc_max(freq, .001f));
            counter = sc_max(1, counter);
            level = fcoin(s1, s2, s3);
        }
        int nsmps = sc_min(remain, counter);
        remain -= nsmps;
        counter -= nsmps;
        LOOP(nsmps, ZXP(out) = level;);
    } while (remain);
    unit->mLevel = level;
    unit->mCounter = counter;
    RPUT
}

void LFClipNoise_Ctor(LFClipNoise* unit) {
    SETCALC(LFClipNoise_next);

    unit->mCounter = 0;
    unit->mLevel = 0.f;

    LFClipNoise_next(unit, 1);
}

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

void LFNoise0_next(LFNoise0* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freq = ZIN0(0);
    float level = unit->mLevel;
    int32 counter = unit->mCounter;
    RGET

        int remain = inNumSamples;
    do {
        if (counter <= 0) {
            counter = (int32)(unit->mRate->mSampleRate / sc_max(freq, .001f));
            counter = sc_max(1, counter);
            level = frand2(s1, s2, s3);
        }
        int nsmps = sc_min(remain, counter);
        remain -= nsmps;
        counter -= nsmps;
        LOOP(nsmps, ZXP(out) = level;);
    } while (remain);
    unit->mLevel = level;
    unit->mCounter = counter;
    RPUT
}

void LFNoise0_next_1(LFNoise0* unit, int inNumSamples) {
    assert(inNumSamples == 1);
    float freq = ZIN0(0);
    float level = unit->mLevel;
    int32 counter = unit->mCounter;

    if (counter <= 0) {
        counter = (int32)(unit->mRate->mSampleRate / sc_max(freq, .001f));
        counter = sc_max(1, counter);
        RGET level = frand2(s1, s2, s3);
        unit->mLevel = level;
        RPUT
    }
    ZOUT0(0) = level;
    counter -= 1;
    unit->mCounter = counter;
}


void LFNoise0_Ctor(LFNoise0* unit) {
    if (BUFLENGTH == 1)
        SETCALC(LFNoise0_next_1);
    else
        SETCALC(LFNoise0_next);

    unit->mCounter = 0;
    unit->mLevel = 0.f;

    LFNoise0_next_1(unit, 1);
}

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

void LFNoise1_next(LFNoise1* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freq = ZIN0(0);
    float level = unit->mLevel;
    float slope = unit->mSlope;
    int32 counter = unit->mCounter;
    RGET

        int remain = inNumSamples;
    do {
        if (counter <= 0) {
            counter = (int32)(unit->mRate->mSampleRate / sc_max(freq, .001f));
            counter = sc_max(1, counter);
            float nextlevel = frand2(s1, s2, s3);
            slope = (nextlevel - level) / counter;
        }
        int nsmps = sc_min(remain, counter);
        remain -= nsmps;
        counter -= nsmps;
        LOOP(nsmps, ZXP(out) = level; level += slope;);
    } while (remain);
    unit->mLevel = level;
    unit->mSlope = slope;
    unit->mCounter = counter;
    RPUT
}

void LFNoise1_Ctor(LFNoise1* unit) {
    SETCALC(LFNoise1_next);

    unit->mCounter = 0;
    unit->mLevel = unit->mParent->mRGen->frand2();
    unit->mSlope = 0.f;

    LFNoise1_next(unit, 1);
}

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

void LFNoise2_next(LFNoise2* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freq = ZIN0(0);
    float level = unit->mLevel;
    float slope = unit->mSlope;
    float curve = unit->mCurve;
    int counter = unit->mCounter;
    RGET

        int remain = inNumSamples;
    do {
        if (counter <= 0) {
            float value = unit->m_nextvalue;
            unit->m_nextvalue = frand2(s1, s2, s3);
            level = unit->m_nextmidpt;
            unit->m_nextmidpt = (unit->m_nextvalue + value) * .5;

            counter = (int32)(unit->mRate->mSampleRate / sc_max(freq, .001f));
            counter = sc_max(2, counter);
            float fseglen = (float)counter;
            curve = 2.f * (unit->m_nextmidpt - level - fseglen * slope) / (fseglen * fseglen + fseglen);
        }
        int nsmps = sc_min(remain, counter);
        remain -= nsmps;
        counter -= nsmps;
        LOOP(nsmps, ZXP(out) = level; slope += curve; level += slope;);
    } while (remain);
    unit->mLevel = level;
    unit->mSlope = slope;
    unit->mCurve = curve;
    unit->mCounter = counter;
    RPUT
}

void LFNoise2_Ctor(LFNoise2* unit) {
    SETCALC(LFNoise2_next);

    unit->mCounter = 0;
    unit->mSlope = 0.f;
    unit->mLevel = 0.f;
    unit->m_nextvalue = unit->mParent->mRGen->frand2();
    unit->m_nextmidpt = unit->m_nextvalue * .5f;

    LFNoise2_next(unit, 1);
}

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

void WrapBufRd_next0(Unit* unit, int inNumSamples);
void WrapBufRd_next0(Unit* unit, int inNumSamples) {
    int bufnum = (int)ZIN0(0);
    float* pos = ZIN(1);

    const SndBuf* buf = unit->mWorld->mSndBufs + bufnum;
    int numchan = buf->channels;

    LOCK_SNDBUF_SHARED(buf);
    if (numchan != unit->mNumOutputs) {
        ClearUnitOutputs(unit, inNumSamples);
        return;
    }

    const float* data = buf->data;
    int numframes = buf->frames;

    float* out[16];
    for (int i = 0; i < numchan; ++i)
        out[i] = OUT(i);

    LOOP1(
        inNumSamples, float fpos = ZXP(pos); int ipos = (int)fpos * numchan; ipos = sc_mod(ipos, numframes);
        int index = numchan * ipos; for (int i = 0; i < numchan; ++i) {
            *++(out[i]) = data[index];
            index++;
        });
}

void ClipBufRd_next0(Unit* unit, int inNumSamples);
void ClipBufRd_next0(Unit* unit, int inNumSamples) {
    int bufnum = (int)ZIN0(0);
    float* pos = ZIN(1);

    const SndBuf* buf = unit->mWorld->mSndBufs + bufnum;
    LOCK_SNDBUF_SHARED(buf);
    int numchan = buf->channels;

    if (numchan != unit->mNumOutputs) {
        ClearUnitOutputs(unit, inNumSamples);
        return;
    }

    const float* data = buf->data;
    int numframes = buf->frames;
    int maxframe = numframes - 2;

    float* out[16];
    for (int i = 0; i < numchan; ++i)
        out[i] = OUT(i);

    LOOP1(
        inNumSamples, float fpos = ZXP(pos); int ipos = (int)fpos * numchan; ipos = sc_clip(ipos, 0, maxframe);
        int index = numchan * ipos; for (int i = 0; i < numchan; ++i) {
            *++(out[i]) = data[index];
            index++;
        });
}

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

PluginLoad(Noise) {
    ft = inTable;

    DefineSimpleUnit(WhiteNoise);
    DefineSimpleUnit(GrayNoise);
    DefineSimpleUnit(ClipNoise);
    DefineSimpleUnit(PinkNoise);
    DefineSimpleUnit(BrownNoise);
    DefineSimpleUnit(Dust);
    DefineSimpleUnit(Dust2);
    DefineSimpleUnit(Crackle);
    DefineSimpleUnit(Logistic);
    DefineSimpleUnit(Hasher);
    DefineSimpleUnit(MantissaMask);
    DefineSimpleUnit(LFClipNoise);
    DefineSimpleUnit(LFNoise0);
    DefineSimpleUnit(LFNoise1);
    DefineSimpleUnit(LFNoise2);
    DefineSimpleUnit(Rand);
    DefineSimpleUnit(IRand);
    DefineSimpleUnit(TRand);
    DefineSimpleUnit(TExpRand);
    DefineSimpleUnit(TIRand);
    DefineSimpleUnit(NRand);
    DefineSimpleUnit(LinRand);
    DefineSimpleUnit(ExpRand);
    DefineSimpleUnit(CoinGate);
    DefineSimpleUnit(RandSeed);
    DefineSimpleUnit(RandID);
}

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