ref: 64d544f911536439cbc718aeb3d46cc6ad2732cb
dir: /LEAF/Src/leaf-envelopes.c/
/* ============================================================================== leaf-envelopes.c Created: 20 Jan 2017 12:02:17pm Author: Michael R Mulshine ============================================================================== */ #if _WIN32 || _WIN64 #include "..\Inc\leaf-envelopes.h" #include "..\Inc\leaf-tables.h" #include "..\leaf.h" #else #include "../Inc/leaf-envelopes.h" #include "../Inc/leaf-tables.h" #include "../leaf.h" #endif // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Envelope ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // void tEnvelope_init(tEnvelope* const envlp, float attack, float decay, oBool loop) { _tEnvelope* env = *envlp = (_tEnvelope*) leaf_alloc(sizeof(_tEnvelope)); env->exp_buff = __leaf_table_exp_decay; env->inc_buff = __leaf_table_attack_decay_inc; env->buff_size = sizeof(__leaf_table_exp_decay); env->loop = loop; if (attack > 8192.0f) attack = 8192.0f; if (attack < 0.0f) attack = 0.0f; if (decay > 8192.0f) decay = 8192.0f; if (decay < 0.0f) decay = 0.0f; int16_t attackIndex = ((int16_t)(attack * 8.0f))-1; int16_t decayIndex = ((int16_t)(decay * 8.0f))-1; int16_t rampIndex = ((int16_t)(2.0f * 8.0f))-1; if (attackIndex < 0) attackIndex = 0; if (decayIndex < 0) decayIndex = 0; if (rampIndex < 0) rampIndex = 0; env->inRamp = OFALSE; env->inAttack = OFALSE; env->inDecay = OFALSE; env->attackInc = env->inc_buff[attackIndex]; env->decayInc = env->inc_buff[decayIndex]; env->rampInc = env->inc_buff[rampIndex]; } void tEnvelope_free(tEnvelope* const envlp) { _tEnvelope* env = *envlp; leaf_free(env); } void tEnvelope_initToPool (tEnvelope* const envlp, float attack, float decay, oBool loop, tMempool* const mp) { _tMempool* m = *mp; _tEnvelope* env = *envlp = (_tEnvelope*) mpool_alloc(sizeof(_tEnvelope), m); env->exp_buff = __leaf_table_exp_decay; env->inc_buff = __leaf_table_attack_decay_inc; env->buff_size = sizeof(__leaf_table_exp_decay); env->loop = loop; if (attack > 8192.0f) attack = 8192.0f; if (attack < 0.0f) attack = 0.0f; if (decay > 8192.0f) decay = 8192.0f; if (decay < 0.0f) decay = 0.0f; int16_t attackIndex = ((int16_t)(attack * 8.0f))-1; int16_t decayIndex = ((int16_t)(decay * 8.0f))-1; int16_t rampIndex = ((int16_t)(2.0f * 8.0f))-1; if (attackIndex < 0) attackIndex = 0; if (decayIndex < 0) decayIndex = 0; if (rampIndex < 0) rampIndex = 0; env->inRamp = OFALSE; env->inAttack = OFALSE; env->inDecay = OFALSE; env->attackInc = env->inc_buff[attackIndex]; env->decayInc = env->inc_buff[decayIndex]; env->rampInc = env->inc_buff[rampIndex]; } void tEnvelope_freeFromPool (tEnvelope* const envlp, tMempool* const mp) { _tMempool* m = *mp; _tEnvelope* env = *envlp; mpool_free(env, m); } void tEnvelope_setAttack(tEnvelope* const envlp, float attack) { _tEnvelope* env = *envlp; int32_t attackIndex; if (attack < 0.0f) { attackIndex = 0.0f; } else if (attack < 8192.0f) { attackIndex = ((int32_t)(attack * 8.0f))-1; } else { attackIndex = ((int32_t)(8192.0f * 8.0f))-1; } env->attackInc = env->inc_buff[attackIndex]; } void tEnvelope_setDecay(tEnvelope* const envlp, float decay) { _tEnvelope* env = *envlp; int32_t decayIndex; if (decay < 0.0f) { decayIndex = 0; } else if (decay < 8192.0f) { decayIndex = ((int32_t)(decay * 8.0f)) - 1; } else { decayIndex = ((int32_t)(8192.0f * 8.0f)) - 1; } env->decayInc = env->inc_buff[decayIndex]; } void tEnvelope_loop(tEnvelope* const envlp, oBool loop) { _tEnvelope* env = *envlp; env->loop = loop; } void tEnvelope_on(tEnvelope* const envlp, float velocity) { _tEnvelope* env = *envlp; if (env->inAttack || env->inDecay) // In case envelope retriggered while it is still happening. { env->rampPhase = 0; env->inRamp = OTRUE; env->rampPeak = env->next; } else // Normal start. { env->inAttack = OTRUE; } env->attackPhase = 0; env->decayPhase = 0; env->inDecay = OFALSE; env->gain = velocity; } float tEnvelope_tick(tEnvelope* const envlp) { _tEnvelope* env = *envlp; if (env->inRamp) { if (env->rampPhase > UINT16_MAX) { env->inRamp = OFALSE; env->inAttack = OTRUE; env->next = 0.0f; } else { env->next = env->rampPeak * env->exp_buff[(uint32_t)env->rampPhase]; } env->rampPhase += env->rampInc; } if (env->inAttack) { // If attack done, time to turn around. if (env->attackPhase > UINT16_MAX) { env->inDecay = OTRUE; env->inAttack = OFALSE; env->next = env->gain * 1.0f; } else { // do interpolation ! env->next = env->gain * env->exp_buff[UINT16_MAX - (uint32_t)env->attackPhase]; // inverted and backwards to get proper rising exponential shape/perception } // Increment envelope attack. env->attackPhase += env->attackInc; } if (env->inDecay) { // If decay done, finish. if (env->decayPhase >= UINT16_MAX) { env->inDecay = OFALSE; if (env->loop) { env->attackPhase = 0; env->decayPhase = 0; env->inAttack = OTRUE; } else { env->next = 0.0f; } } else { env->next = env->gain * (env->exp_buff[(uint32_t)env->decayPhase]); // do interpolation ! } // Increment envelope decay; env->decayPhase += env->decayInc; } return env->next; } /* ADSR */ void tADSR_init(tADSR* const adsrenv, float attack, float decay, float sustain, float release) { tADSR_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool); } void tADSR_free(tADSR* const adsrenv) { tADSR_freeFromPool(adsrenv, &leaf.mempool); } void tADSR_initToPool (tADSR* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp) { _tMempool* m = *mp; _tADSR* adsr = *adsrenv = (_tADSR*) mpool_alloc(sizeof(_tADSR), m); adsr->exp_buff = __leaf_table_exp_decay; adsr->inc_buff = __leaf_table_attack_decay_inc; adsr->buff_size = sizeof(__leaf_table_exp_decay); if (attack > 8192.0f) attack = 8192.0f; if (attack < 0.0f) attack = 0.0f; if (decay > 8192.0f) decay = 8192.0f; if (decay < 0.0f) decay = 0.0f; if (sustain > 1.0f) sustain = 1.0f; if (sustain < 0.0f) sustain = 0.0f; if (release > 8192.0f) release = 8192.0f; if (release < 0.0f) release = 0.0f; int16_t attackIndex = ((int16_t)(attack * 8.0f))-1; int16_t decayIndex = ((int16_t)(decay * 8.0f))-1; int16_t releaseIndex = ((int16_t)(release * 8.0f))-1; int16_t rampIndex = ((int16_t)(2.0f * 8.0f))-1; if (attackIndex < 0) attackIndex = 0; if (decayIndex < 0) decayIndex = 0; if (releaseIndex < 0) releaseIndex = 0; if (rampIndex < 0) rampIndex = 0; adsr->next = 0.0f; adsr->inRamp = OFALSE; adsr->inAttack = OFALSE; adsr->inDecay = OFALSE; adsr->inSustain = OFALSE; adsr->inRelease = OFALSE; adsr->sustain = sustain; adsr->attackInc = adsr->inc_buff[attackIndex]; adsr->decayInc = adsr->inc_buff[decayIndex]; adsr->releaseInc = adsr->inc_buff[releaseIndex]; adsr->rampInc = adsr->inc_buff[rampIndex]; adsr->leakFactor = 1.0f; } void tADSR_freeFromPool (tADSR* const adsrenv, tMempool* const mp) { _tMempool* m = *mp; _tADSR* adsr = *adsrenv; mpool_free(adsr, m); } void tADSR_setAttack(tADSR* const adsrenv, float attack) { _tADSR* adsr = *adsrenv; int32_t attackIndex; if (attack < 0.0f) { attackIndex = 0.0f; } else if (attack < 8192.0f) { attackIndex = ((int32_t)(attack * 8.0f))-1; } else { attackIndex = ((int32_t)(8192.0f * 8.0f))-1; } adsr->attackInc = adsr->inc_buff[attackIndex]; } void tADSR_setDecay(tADSR* const adsrenv, float decay) { _tADSR* adsr = *adsrenv; int32_t decayIndex; if (decay < 0.0f) { decayIndex = 0.0f; } else if (decay < 8192.0f) { decayIndex = ((int32_t)(decay * 8.0f)) - 1; } else { decayIndex = ((int32_t)(8192.0f * 8.0f)) - 1; } adsr->decayInc = adsr->inc_buff[decayIndex]; } void tADSR_setSustain(tADSR* const adsrenv, float sustain) { _tADSR* adsr = *adsrenv; if (sustain > 1.0f) adsr->sustain = 1.0f; else if (sustain < 0.0f) adsr->sustain = 0.0f; else adsr->sustain = sustain; } void tADSR_setRelease(tADSR* const adsrenv, float release) { _tADSR* adsr = *adsrenv; int32_t releaseIndex; if (release < 0.0f) { releaseIndex = 0.0f; } else if (release < 8192.0f) { releaseIndex = ((int32_t)(release * 8.0f)) - 1; } else { releaseIndex = ((int32_t)(8192.0f * 8.0f)) - 1; } adsr->releaseInc = adsr->inc_buff[releaseIndex]; } // 0.999999 is slow leak, 0.9 is fast leak void tADSR_setLeakFactor(tADSR* const adsrenv, float leakFactor) { _tADSR* adsr = *adsrenv; adsr->leakFactor = leakFactor; } void tADSR_on(tADSR* const adsrenv, float velocity) { _tADSR* adsr = *adsrenv; if ((adsr->inAttack || adsr->inDecay) || (adsr->inSustain || adsr->inRelease)) // In case ADSR retriggered while it is still happening. { adsr->rampPhase = 0; adsr->inRamp = OTRUE; adsr->rampPeak = adsr->next; } else // Normal start. { adsr->inAttack = OTRUE; } adsr->attackPhase = 0; adsr->decayPhase = 0; adsr->releasePhase = 0; adsr->inDecay = OFALSE; adsr->inSustain = OFALSE; adsr->inRelease = OFALSE; adsr->gain = velocity; } void tADSR_off(tADSR* const adsrenv) { _tADSR* adsr = *adsrenv; if (adsr->inRelease) return; adsr->inAttack = OFALSE; adsr->inDecay = OFALSE; adsr->inSustain = OFALSE; adsr->inRelease = OTRUE; adsr->releasePeak = adsr->next; } float tADSR_tick(tADSR* const adsrenv) { _tADSR* adsr = *adsrenv; if (adsr->inRamp) { if (adsr->rampPhase > UINT16_MAX) { adsr->inRamp = OFALSE; adsr->inAttack = OTRUE; adsr->next = 0.0f; } else { adsr->next = adsr->rampPeak * adsr->exp_buff[(uint32_t)adsr->rampPhase]; } adsr->rampPhase += adsr->rampInc; } if (adsr->inAttack) { // If attack done, time to turn around. if (adsr->attackPhase > UINT16_MAX) { adsr->inDecay = OTRUE; adsr->inAttack = OFALSE; adsr->next = adsr->gain * 1.0f; } else { // do interpolation ! adsr->next = adsr->gain * adsr->exp_buff[UINT16_MAX - (uint32_t)adsr->attackPhase]; // inverted and backwards to get proper rising exponential shape/perception } // Increment ADSR attack. adsr->attackPhase += adsr->attackInc; } if (adsr->inDecay) { // If decay done, sustain. if (adsr->decayPhase >= UINT16_MAX) { adsr->inDecay = OFALSE; adsr->inSustain = OTRUE; adsr->next = adsr->gain * adsr->sustain; } else { adsr->next = (adsr->gain * (adsr->sustain + ((adsr->exp_buff[(uint32_t)adsr->decayPhase]) * (1.0f - adsr->sustain)))) * adsr->leakFactor; // do interpolation ! } // Increment ADSR decay. adsr->decayPhase += adsr->decayInc; } if (adsr->inSustain) { adsr->next = adsr->next * adsr->leakFactor; } if (adsr->inRelease) { // If release done, finish. if (adsr->releasePhase >= UINT16_MAX) { adsr->inRelease = OFALSE; adsr->next = 0.0f; } else { adsr->next = adsr->releasePeak * (adsr->exp_buff[(uint32_t)adsr->releasePhase]); // do interpolation ! } // Increment envelope release; adsr->releasePhase += adsr->releaseInc; } return adsr->next; } /* ADSR 2*/ //This one is adapted from the VCV Rack code //-JS const float ADSR2_MIN_TIME = 1e-3f; const float ADSR2_INV_MIN_TIME = 1000.0f; const float ADSR2_MAX_TIME = 10.f; const float ADSR2_LAMBDA_BASE = 10000.0f; void tADSR2_init(tADSR2* const adsrenv, float attack, float decay, float sustain, float release) { tADSR2_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool); } void tADSR2_free(tADSR2* const adsrenv) { tADSR2_freeFromPool(adsrenv, &leaf.mempool); } void tADSR2_initToPool (tADSR2* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp) { _tMempool* m = *mp; _tADSR2* adsr = *adsrenv = (_tADSR2*) mpool_alloc(sizeof(_tADSR2), m); adsr->sampleRateInMs = leaf.sampleRate * 0.001f; adsr->attack = LEAF_clip(0.0f, attack * 0.001f, 1.0f); adsr->attackLambda = powf(ADSR2_LAMBDA_BASE, -adsr->attack) * ADSR2_INV_MIN_TIME; adsr->decay = LEAF_clip(0.0f, decay * 0.001f, 1.0f); adsr->decayLambda = powf(ADSR2_LAMBDA_BASE, -adsr->decay) * ADSR2_INV_MIN_TIME; adsr->sustain= sustain; adsr->release = LEAF_clip(0.0f, release * 0.001f, 1.0f); adsr->releaseLambda = powf(ADSR2_LAMBDA_BASE, -adsr->release) * ADSR2_INV_MIN_TIME; adsr->attacking = 0; adsr->gate = 0; adsr->gain = 1.0f; adsr->targetGainSquared = 1.0f; adsr->factor = 0.01f; adsr->oneMinusFactor = 0.99f; adsr->env = 0.0f; adsr->leakFactor = 1.0f; } void tADSR2_freeFromPool (tADSR2* const adsrenv, tMempool* const mp) { _tMempool* m = *mp; _tADSR2* adsr = *adsrenv; mpool_free(adsr, m); } void tADSR2_setAttack(tADSR2* const adsrenv, float attack) { _tADSR2* adsr = *adsrenv; adsr->attack = LEAF_clip(0.0f, attack * 0.001f, 1.0f); adsr->attackLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->attack) * ADSR2_INV_MIN_TIME; } void tADSR2_setDecay(tADSR2* const adsrenv, float decay) { _tADSR2* adsr = *adsrenv; adsr->decay = LEAF_clip(0.0f, decay * 0.001f, 1.0f); adsr->decayLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->decay) * ADSR2_INV_MIN_TIME; } void tADSR2_setSustain(tADSR2* const adsrenv, float sustain) { _tADSR2* adsr = *adsrenv; adsr->sustain = sustain; if (adsr->gate) { if (adsr->attacking == 0) { adsr->envTarget = sustain; } } } void tADSR2_setRelease(tADSR2* const adsrenv, float release) { _tADSR2* adsr = *adsrenv; adsr->release = LEAF_clip(0.0f, release * 0.001f, 1.0f); adsr->releaseLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->release) * ADSR2_INV_MIN_TIME; } // 0.999999 is slow leak, 0.9 is fast leak void tADSR2_setLeakFactor(tADSR2* const adsrenv, float leakFactor) { _tADSR2* adsr = *adsrenv; adsr->leakFactor = leakFactor; } void tADSR2_on(tADSR2* const adsrenv, float velocity) { _tADSR2* adsr = *adsrenv; adsr->targetGainSquared = velocity * velocity; adsr->envTarget = 1.2f; adsr->leakGain = 1.0f; adsr->gate = 1; adsr->attacking = 1; } void tADSR2_off(tADSR2* const adsrenv) { _tADSR2* adsr = *adsrenv; adsr->gate = 0; adsr->envTarget = 0.0f; } float tADSR2_tick(tADSR2* const adsrenv) { _tADSR2* adsr = *adsrenv; float lambda; if (adsr->gate) { if (adsr->attacking) { lambda = adsr->attackLambda; } else { lambda = adsr->decayLambda; } } else { lambda = adsr->releaseLambda; } // Adjust env adsr->env += (adsr->envTarget - adsr->env) * lambda * leaf.invSampleRate; // Turn off attacking state if envelope is HIGH if (adsr->env >= 1.0f) { adsr->attacking = 0; adsr->envTarget = adsr->sustain; } //smooth the gain value -- this is not ideal, a retrigger while the envelope is still going with a new gain will cause a jump, although it will be smoothed quickly. Maybe doing the math so the range is computed based on the gain rather than 0.->1. is preferable? But that's harder to get the exponential curve right without a lookup. adsr->gain = ((adsr->factor*adsr->targetGainSquared)+(adsr->oneMinusFactor*adsr->gain)); adsr->leakGain *= adsr->leakFactor; return adsr->env * adsr->gain * adsr->leakGain; } /* ADSR 3*/ //This one doesn't use any lookup table - by Nigel Redmon from his blog. Thanks, Nigel! //-JS float calcADSR3Coef(double rate, double targetRatio) { return (rate <= 0.0f) ? 0.0f : exp(-log((1.0 + targetRatio) / targetRatio) / rate); } void tADSR3_init(tADSR3* const adsrenv, float attack, float decay, float sustain, float release) { tADSR3_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool); } void tADSR3_free(tADSR3* const adsrenv) { tADSR3_freeFromPool(adsrenv, &leaf.mempool); } void tADSR3_initToPool (tADSR3* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp) { _tMempool* m = *mp; _tADSR3* adsr = *adsrenv = (_tADSR3*) mpool_alloc(sizeof(_tADSR3), m); adsr->sampleRateInMs = leaf.sampleRate * 0.001f; adsr->targetRatioA = 0.3f; adsr->targetRatioDR = 0.0001f; adsr->attackRate = attack * adsr->sampleRateInMs; adsr->attackCoef = calcADSR3Coef(attack * adsr->sampleRateInMs, adsr->targetRatioA); adsr->attackBase = (1.0f + adsr->targetRatioA) * (1.0f - adsr->attackCoef); adsr->decayRate = decay * adsr->sampleRateInMs; adsr->decayCoef = calcADSR3Coef(decay * adsr->sampleRateInMs,adsr-> targetRatioDR); adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef); adsr->sustainLevel = sustain; adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef); adsr->releaseRate = release * adsr->sampleRateInMs; adsr->releaseCoef = calcADSR3Coef(release * adsr->sampleRateInMs, adsr->targetRatioDR); adsr->releaseBase = -adsr->targetRatioDR * (1.0f - adsr->releaseCoef); adsr->state = env_idle; adsr->gain = 1.0f; adsr->targetGainSquared = 1.0f; adsr->factor = 0.01f; adsr->oneMinusFactor = 0.99f; adsr->output = 0.0f; adsr->leakFactor = 1.0f; } void tADSR3_freeFromPool (tADSR3* const adsrenv, tMempool* const mp) { _tMempool* m = *mp; _tADSR3* adsr = *adsrenv; mpool_free(adsr, m); } void tADSR3_setAttack(tADSR3* const adsrenv, float attack) { _tADSR3* adsr = *adsrenv; adsr->attackRate = attack * adsr->sampleRateInMs; adsr->attackCoef = calcADSR3Coef(adsr->attackRate, adsr->targetRatioA); adsr->attackBase = (1.0f + adsr->targetRatioA) * (1.0f - adsr->attackCoef); } void tADSR3_setDecay(tADSR3* const adsrenv, float decay) { _tADSR3* adsr = *adsrenv; adsr->decayRate = decay * adsr->sampleRateInMs; adsr->decayCoef = calcADSR3Coef(adsr->decayRate,adsr-> targetRatioDR); adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef); } void tADSR3_setSustain(tADSR3* const adsrenv, float sustain) { _tADSR3* adsr = *adsrenv; adsr->sustainLevel = sustain; adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef); } void tADSR3_setRelease(tADSR3* const adsrenv, float release) { _tADSR3* adsr = *adsrenv; adsr->releaseRate = release * adsr->sampleRateInMs; adsr->releaseCoef = calcADSR3Coef(adsr->releaseRate, adsr->targetRatioDR); adsr->releaseBase = -adsr->targetRatioDR * (1.0f - adsr->releaseCoef); } // 0.999999 is slow leak, 0.9 is fast leak void tADSR3_setLeakFactor(tADSR3* const adsrenv, float leakFactor) { _tADSR3* adsr = *adsrenv; adsr->leakFactor = leakFactor; } void tADSR3_on(tADSR3* const adsrenv, float velocity) { _tADSR3* adsr = *adsrenv; adsr->state = env_attack; adsr->targetGainSquared = velocity * velocity; } void tADSR3_off(tADSR3* const adsrenv) { _tADSR3* adsr = *adsrenv; if (adsr->state != env_idle) { adsr->state = env_release; } } float tADSR3_tick(tADSR3* const adsrenv) { _tADSR3* adsr = *adsrenv; switch (adsr->state) { case env_idle: break; case env_attack: adsr->output = adsr->attackBase + adsr->output * adsr->attackCoef; if (adsr->output >= 1.0f) { adsr->output = 1.0f; adsr->state = env_decay; } break; case env_decay: adsr->output = adsr->decayBase + adsr->output * adsr->decayCoef * adsr->leakFactor; if (adsr->output <= adsr->sustainLevel) { adsr->output = adsr->sustainLevel; adsr->state = env_sustain; } break; case env_sustain: adsr->output = adsr->output * adsr->leakFactor; break; case env_release: adsr->output = adsr->releaseBase + adsr->output * adsr->releaseCoef; if (adsr->output <= 0.0f) { adsr->output = 0.0f; adsr->state = env_idle; } } //smooth the gain value -- this is not ideal, a retrigger while the envelope is still going with a new gain will cause a jump, although it will be smoothed quickly. Maybe doing the math so the range is computed based on the gain rather than 0.->1. is preferable? But that's harder to get the exponential curve right without a lookup. adsr->gain = (adsr->factor*adsr->targetGainSquared)+(adsr->oneMinusFactor*adsr->gain); return adsr->output * adsr->gain; } /* ADSR 4 */ // new version of our original table-based ADSR but with the table passed in by the user // use this if the size of the big ADSR tables is too much. void tADSR4_init (tADSR4* const adsrenv, float attack, float decay, float sustain, float release, float* expBuffer, int bufferSize) { tADSR4_initToPool (adsrenv, attack, decay, sustain, release, expBuffer, bufferSize, &leaf.mempool); } void tADSR4_free(tADSR4* const adsrenv) { tADSR4_freeFromPool(adsrenv, &leaf.mempool); } //initialize with an exponential function that decays -- i.e. a call to LEAF_generate_exp(expBuffer, 0.001f, 0.0f, 1.0f, -0.0008f, EXP_BUFFER_SIZE); //times are in ms void tADSR4_initToPool (tADSR4* const adsrenv, float attack, float decay, float sustain, float release, float* expBuffer, int bufferSize, tMempool* const mp) { _tMempool* m = *mp; _tADSR4* adsr = *adsrenv = (_tADSR4*) mpool_alloc(sizeof(_tADSR4), m); adsr->exp_buff = expBuffer; adsr->buff_size = bufferSize; adsr->buff_sizeMinusOne = bufferSize - 1; adsr->bufferSizeDividedBySampleRateInMs = bufferSize / (leaf.sampleRate * 0.001f); if (attack < 0.0f) attack = 0.0f; if (decay < 0.0f) decay = 0.0f; if (sustain > 1.0f) sustain = 1.0f; if (sustain < 0.0f) sustain = 0.0f; if (release < 0.0f) release = 0.0f; adsr->next = 0.0f; adsr->inRamp = OFALSE; adsr->inAttack = OFALSE; adsr->inDecay = OFALSE; adsr->inSustain = OFALSE; adsr->inRelease = OFALSE; adsr->sustain = sustain; adsr->attackInc = adsr->bufferSizeDividedBySampleRateInMs / attack; adsr->decayInc = adsr->bufferSizeDividedBySampleRateInMs / decay; adsr->releaseInc = adsr->bufferSizeDividedBySampleRateInMs / release; adsr->rampInc = adsr->bufferSizeDividedBySampleRateInMs / 8.0f; adsr->leakFactor = 1.0f; } void tADSR4_freeFromPool (tADSR4* const adsrenv, tMempool* const mp) { _tMempool* m = *mp; _tADSR4* adsr = *adsrenv; mpool_free(adsr, m); } void tADSR4_setAttack(tADSR4* const adsrenv, float attack) { _tADSR4* adsr = *adsrenv; if (attack < 0.0f) { attack = 0.0f; } adsr->attackInc = adsr->bufferSizeDividedBySampleRateInMs / attack; } void tADSR4_setDecay(tADSR4* const adsrenv, float decay) { _tADSR4* adsr = *adsrenv; if (decay < 0.0f) { decay = 0.0f; } adsr->decayInc = adsr->bufferSizeDividedBySampleRateInMs / decay; } void tADSR4_setSustain(tADSR4* const adsrenv, float sustain) { _tADSR4* adsr = *adsrenv; if (sustain > 1.0f) adsr->sustain = 1.0f; else if (sustain < 0.0f) adsr->sustain = 0.0f; else adsr->sustain = sustain; } void tADSR4_setRelease(tADSR4* const adsrenv, float release) { _tADSR4* adsr = *adsrenv; if (release < 0.0f) { release = 0.0f; } adsr->releaseInc = adsr->bufferSizeDividedBySampleRateInMs / release; } // 0.999999 is slow leak, 0.9 is fast leak void tADSR4_setLeakFactor(tADSR4* const adsrenv, float leakFactor) { _tADSR4* adsr = *adsrenv; adsr->leakFactor = leakFactor; } void tADSR4_on(tADSR4* const adsrenv, float velocity) { _tADSR4* adsr = *adsrenv; if ((adsr->inAttack || adsr->inDecay) || (adsr->inSustain || adsr->inRelease)) // In case ADSR retriggered while it is still happening. { adsr->rampPhase = 0; adsr->inRamp = OTRUE; adsr->rampPeak = adsr->next; } else // Normal start. { adsr->inAttack = OTRUE; } adsr->attackPhase = 0; adsr->decayPhase = 0; adsr->releasePhase = 0; adsr->inDecay = OFALSE; adsr->inSustain = OFALSE; adsr->inRelease = OFALSE; adsr->gain = velocity; } void tADSR4_off(tADSR4* const adsrenv) { _tADSR4* adsr = *adsrenv; if (adsr->inRelease) return; adsr->inAttack = OFALSE; adsr->inDecay = OFALSE; adsr->inSustain = OFALSE; adsr->inRelease = OTRUE; adsr->releasePeak = adsr->next; } float tADSR4_tick(tADSR4* const adsrenv) { _tADSR4* adsr = *adsrenv; if (adsr->inRamp) { if (adsr->rampPhase > adsr->buff_sizeMinusOne) { adsr->inRamp = OFALSE; adsr->inAttack = OTRUE; adsr->next = 0.0f; } else { uint32_t intPart = (uint32_t)adsr->rampPhase; float floatPart = adsr->rampPhase - intPart; float secondValue; if (adsr->rampPhase + 1.0f > adsr->buff_sizeMinusOne) { secondValue = 0.0f; } else { secondValue = adsr->exp_buff[(uint32_t)((adsr->rampPhase)+1)]; } adsr->next = adsr->rampPeak * LEAF_interpolation_linear(adsr->exp_buff[(uint32_t)(adsr->rampPhase)], secondValue, floatPart); } adsr->rampPhase += adsr->rampInc; } if (adsr->inAttack) { // If attack done, time to turn around. if (adsr->attackPhase > adsr->buff_sizeMinusOne) { adsr->inDecay = OTRUE; adsr->inAttack = OFALSE; adsr->next = adsr->gain; } else { // do interpolation ! uint32_t intPart = (uint32_t)adsr->attackPhase; float floatPart = adsr->attackPhase - intPart; float secondValue; if (adsr->attackPhase + 1.0f > adsr->buff_sizeMinusOne) { secondValue = 0.0f; } else { secondValue = adsr->exp_buff[(uint32_t)((adsr->attackPhase)+1)]; } adsr->next = adsr->gain * (1.0f - LEAF_interpolation_linear(adsr->exp_buff[(uint32_t)(adsr->attackPhase)], secondValue, floatPart)); // inverted and backwards to get proper rising exponential shape/perception } // Increment ADSR attack. adsr->attackPhase += adsr->attackInc; } if (adsr->inDecay) { // If decay done, sustain. if (adsr->decayPhase > adsr->buff_sizeMinusOne) { adsr->inDecay = OFALSE; adsr->inSustain = OTRUE; adsr->next = adsr->gain * adsr->sustain; } else { uint32_t intPart = (uint32_t)adsr->decayPhase; float floatPart = adsr->decayPhase - intPart; float secondValue; if (adsr->decayPhase + 1.0f > adsr->buff_sizeMinusOne) { secondValue = 0.0f; } else { secondValue = adsr->exp_buff[(uint32_t)((adsr->decayPhase)+1)]; } float interpValue = (LEAF_interpolation_linear(adsr->exp_buff[(uint32_t)(adsr->decayPhase)], secondValue, floatPart)); adsr->next = (adsr->gain * (adsr->sustain + (interpValue * (1.0f - adsr->sustain)))) * adsr->leakFactor; // do interpolation ! } // Increment ADSR decay. adsr->decayPhase += adsr->decayInc; } if (adsr->inSustain) { adsr->next = adsr->next * adsr->leakFactor; } if (adsr->inRelease) { // If release done, finish. if (adsr->releasePhase > adsr->buff_sizeMinusOne) { adsr->inRelease = OFALSE; adsr->next = 0.0f; } else { uint32_t intPart = (uint32_t)adsr->releasePhase; float floatPart = adsr->releasePhase - intPart; float secondValue; if (adsr->releasePhase + 1.0f > adsr->buff_sizeMinusOne) { secondValue = 0.0f; } else { secondValue = adsr->exp_buff[(uint32_t)((adsr->releasePhase)+1)]; } adsr->next = adsr->releasePeak * (LEAF_interpolation_linear(adsr->exp_buff[(uint32_t)(adsr->releasePhase)], secondValue, floatPart)); // do interpolation ! } // Increment envelope release; adsr->releasePhase += adsr->releaseInc; } return adsr->next; } float tADSR4_tickNoInterp(tADSR4* const adsrenv) { _tADSR4* adsr = *adsrenv; if (adsr->inRamp) { if (adsr->rampPhase > adsr->buff_sizeMinusOne) { adsr->inRamp = OFALSE; adsr->inAttack = OTRUE; adsr->next = 0.0f; } else { adsr->next = adsr->rampPeak * adsr->exp_buff[(uint32_t)(adsr->rampPhase)]; } adsr->rampPhase += adsr->rampInc; } if (adsr->inAttack) { // If attack done, time to turn around. if (adsr->attackPhase > adsr->buff_sizeMinusOne) { adsr->inDecay = OTRUE; adsr->inAttack = OFALSE; adsr->next = adsr->gain; } else { // do interpolation ! adsr->next = adsr->gain * (1.0f - adsr->exp_buff[(uint32_t)(adsr->attackPhase)]); // inverted and backwards to get proper rising exponential shape/perception } // Increment ADSR attack. adsr->attackPhase += adsr->attackInc; } if (adsr->inDecay) { // If decay done, sustain. if (adsr->decayPhase > adsr->buff_sizeMinusOne) { adsr->inDecay = OFALSE; adsr->inSustain = OTRUE; adsr->next = adsr->gain * adsr->sustain; } else { adsr->next = (adsr->gain * (adsr->sustain + ((adsr->exp_buff[(uint32_t)(adsr->decayPhase)]) * (1.0f - adsr->sustain)))) * adsr->leakFactor; // do interpolation ! } // Increment ADSR decay. adsr->decayPhase += adsr->decayInc; } if (adsr->inSustain) { adsr->next = adsr->next * adsr->leakFactor; } if (adsr->inRelease) { // If release done, finish. if (adsr->releasePhase > adsr->buff_sizeMinusOne) { adsr->inRelease = OFALSE; adsr->next = 0.0f; } else { adsr->next = adsr->releasePeak * (adsr->exp_buff[(uint32_t)(adsr->releasePhase)]); // do interpolation ! } // Increment envelope release; adsr->releasePhase += adsr->releaseInc; } return adsr->next; } /////----------------- /* Ramp */ void tRamp_init(tRamp* const r, float time, int samples_per_tick) { _tRamp* ramp = *r = (_tRamp*) leaf_alloc(sizeof(_tRamp)); ramp->inv_sr_ms = 1.0f/(leaf.sampleRate*0.001f); ramp->minimum_time = ramp->inv_sr_ms * samples_per_tick; ramp->curr = 0.0f; ramp->dest = 0.0f; if (time < ramp->minimum_time) { ramp->time = ramp->minimum_time; } else { ramp->time = time; } ramp->factor = (1.0f / ramp->time) * ramp->inv_sr_ms; ramp->samples_per_tick = samples_per_tick; ramp->inc = ((ramp->dest - ramp->curr) / ramp->time * ramp->inv_sr_ms) * (float)ramp->samples_per_tick; } void tRamp_free(tRamp* const r) { _tRamp* ramp = *r; leaf_free(ramp); } void tRamp_initToPool (tRamp* const r, float time, int samples_per_tick, tMempool* const mp) { _tMempool* m = *mp; _tRamp* ramp = *r = (_tRamp*) mpool_alloc(sizeof(_tRamp), m); ramp->inv_sr_ms = 1.0f/(leaf.sampleRate*0.001f); ramp->minimum_time = ramp->inv_sr_ms * samples_per_tick; ramp->curr = 0.0f; ramp->dest = 0.0f; if (time < ramp->minimum_time) { ramp->time = ramp->minimum_time; } else { ramp->time = time; } ramp->samples_per_tick = samples_per_tick; ramp->factor = (1.0f / ramp->time) * ramp->inv_sr_ms * (float)ramp->samples_per_tick; ramp->inc = (ramp->dest - ramp->curr) * ramp->factor; } void tRamp_freeFromPool (tRamp* const r, tMempool* const mp) { _tMempool* m = *mp; _tRamp* ramp = *r; mpool_free(ramp, m); } void tRamp_setTime(tRamp* const ramp, float time) { _tRamp* r = *ramp; if (time < r->minimum_time) { r->time = r->minimum_time; } else { r->time = time; } r->factor = (1.0f / r->time) * r->inv_sr_ms * (float)r->samples_per_tick; r->inc = (r->dest - r->curr) * r->factor; } void tRamp_setDest(tRamp* const ramp, float dest) { _tRamp* r = *ramp; r->dest = dest; r->inc = (r->dest - r->curr) * r->factor; } void tRamp_setVal(tRamp* const ramp, float val) { _tRamp* r = *ramp; r->curr = val; r->inc = (r->dest - r->curr) * r->factor; } float tRamp_tick(tRamp* const ramp) { _tRamp* r = *ramp; r->curr += r->inc; if (((r->curr >= r->dest) && (r->inc > 0.0f)) || ((r->curr <= r->dest) && (r->inc < 0.0f))) { r->inc = 0.0f; r->curr=r->dest; } return r->curr; } float tRamp_sample(tRamp* const ramp) { _tRamp* r = *ramp; return r->curr; } void tRampSampleRateChanged(tRamp* const ramp) { _tRamp* r = *ramp; r->inv_sr_ms = 1.0f / (leaf.sampleRate * 0.001f); r->factor = (1.0f / r->time) * r->inv_sr_ms * (float)r->samples_per_tick; r->inc = (r->dest - r->curr) * r->factor; } /* RampUpDown */ void tRampUpDown_init(tRampUpDown* const r, float upTime, float downTime, int samples_per_tick) { tRampUpDown_initToPool(r, upTime, downTime, samples_per_tick, &leaf.mempool); } void tRampUpDown_free(tRampUpDown* const r) { tRampUpDown_freeFromPool(r, &leaf.mempool); } void tRampUpDown_initToPool(tRampUpDown* const r, float upTime, float downTime, int samples_per_tick, tMempool* const mp) { _tMempool* m = *mp; _tRampUpDown* ramp = *r = (_tRampUpDown*) mpool_alloc(sizeof(_tRampUpDown), m); ramp->inv_sr_ms = 1.0f/(leaf.sampleRate*0.001f); ramp->minimum_time = ramp->inv_sr_ms * samples_per_tick; ramp->curr = 0.0f; ramp->dest = 0.0f; if (upTime < ramp->minimum_time) { ramp->upTime = ramp->minimum_time; } else { ramp->upTime = upTime; } if (downTime < ramp->minimum_time) { ramp->downTime = ramp->minimum_time; } else { ramp->downTime = downTime; } ramp->samples_per_tick = samples_per_tick; ramp->upInc = ((ramp->dest - ramp->curr) / ramp->upTime * ramp->inv_sr_ms) * (float)ramp->samples_per_tick; ramp->downInc = ((ramp->dest - ramp->curr) / ramp->downTime * ramp->inv_sr_ms) * (float)ramp->samples_per_tick; } void tRampUpDown_freeFromPool (tRampUpDown* const r, tMempool* const mp) { _tMempool* m = *mp; _tRampUpDown* ramp = *r; mpool_free(ramp, m); } void tRampUpDown_setUpTime(tRampUpDown* const ramp, float upTime) { _tRampUpDown* r = *ramp; if (upTime < r->minimum_time) { r->upTime = r->minimum_time; } else { r->upTime = upTime; } r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick; } void tRampUpDown_setDownTime(tRampUpDown* const ramp, float downTime) { _tRampUpDown* r = *ramp; if (downTime < r->minimum_time) { r->downTime = r->minimum_time; } else { r->downTime = downTime; } r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick; } void tRampUpDown_setDest(tRampUpDown* const ramp, float dest) { _tRampUpDown* r = *ramp; r->dest = dest; r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick; r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick; } void tRampUpDown_setVal(tRampUpDown* const ramp, float val) { _tRampUpDown* r = *ramp; r->curr = val; r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick; r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick; } float tRampUpDown_tick(tRampUpDown* const ramp) { _tRampUpDown* r = *ramp; float test; if (r->dest < r->curr) { test = r->curr + r->downInc; if (test > r->dest) { r->curr = test; } else { r->downInc = 0.0f; r->curr = r->dest; } } else if (r->dest > r->curr) { test = r->curr + r->upInc; if (test < r->dest) { r->curr = test; } else { r->upInc = 0.0f; r->curr = r->dest; } } return r->curr; } float tRampUpDown_sample(tRampUpDown* const ramp) { _tRampUpDown* r = *ramp; return r->curr; } /* Exponential Smoother */ void tExpSmooth_init(tExpSmooth* const expsmooth, float val, float factor) { // factor is usually a value between 0 and 0.1. Lower value is slower. 0.01 for example gives you a smoothing time of about 10ms _tExpSmooth* smooth = *expsmooth = (_tExpSmooth*) leaf_alloc(sizeof(_tExpSmooth)); smooth->curr=val; smooth->dest=val; if (factor<0) factor=0; if (factor>1) factor=1; smooth->factor=factor; smooth->oneminusfactor=1.0f-factor; } void tExpSmooth_free(tExpSmooth* const expsmooth) { _tExpSmooth* smooth = *expsmooth; leaf_free(smooth); } void tExpSmooth_initToPool (tExpSmooth* const expsmooth, float val, float factor, tMempool* const mp) { _tMempool* m = *mp; _tExpSmooth* smooth = *expsmooth = (_tExpSmooth*) mpool_alloc(sizeof(_tExpSmooth), m); smooth->curr=val; smooth->dest=val; if (factor<0) factor=0; if (factor>1) factor=1; smooth->factor=factor; smooth->oneminusfactor=1.0f-factor; } void tExpSmooth_freeFromPool (tExpSmooth* const expsmooth, tMempool* const mp) { _tMempool* m = *mp; _tExpSmooth* smooth = *expsmooth; mpool_free(smooth, m); } void tExpSmooth_setFactor(tExpSmooth* const expsmooth, float factor) { // factor is usually a value between 0 and 0.1. Lower value is slower. 0.01 for example gives you a smoothing time of about 10ms _tExpSmooth* smooth = *expsmooth; if (factor<0) factor=0; else if (factor>1) factor=1; smooth->factor=factor; smooth->oneminusfactor=1.0f-factor; } void tExpSmooth_setDest(tExpSmooth* const expsmooth, float dest) { _tExpSmooth* smooth = *expsmooth; smooth->dest=dest; } void tExpSmooth_setVal(tExpSmooth* const expsmooth, float val) { _tExpSmooth* smooth = *expsmooth; smooth->curr=val; } void tExpSmooth_setValAndDest(tExpSmooth* const expsmooth, float val) { _tExpSmooth* smooth = *expsmooth; smooth->curr=val; smooth->dest=val; } float tExpSmooth_tick(tExpSmooth* const expsmooth) { _tExpSmooth* smooth = *expsmooth; smooth->curr = smooth->factor*smooth->dest+smooth->oneminusfactor*smooth->curr; return smooth->curr; } float tExpSmooth_sample(tExpSmooth* const expsmooth) { _tExpSmooth* smooth = *expsmooth; return smooth->curr; } //tSlide is based on the max/msp slide~ object //// void tSlide_init (tSlide* const sl, float upSlide, float downSlide) { tSlide_initToPool (sl, upSlide, downSlide, &leaf.mempool); } void tSlide_free (tSlide* const sl) { tSlide_freeFromPool (sl, &leaf.mempool); } void tSlide_initToPool (tSlide* const sl, float upSlide, float downSlide, tMempool* const mp) { _tMempool* m = *mp; _tSlide* s = *sl = (_tSlide*) mpool_alloc(sizeof(_tSlide), m); s->prevIn = 0.0f; s->currentOut = 0.0f; s->prevOut = 0.0f; if (upSlide < 1.0f) { upSlide = 1.0f; } if (downSlide < 1.0f) { downSlide = 1.0f; } s->invUpSlide = 1.0f / upSlide; s->invDownSlide = 1.0f / downSlide; } void tSlide_freeFromPool (tSlide* const sl, tMempool* const mp) { _tMempool* m = *mp; _tSlide* s = *sl; mpool_free(s, m); } float tSlide_tick(tSlide* const sl, float in) { _tSlide* s = *sl; if (in >= s->prevOut) { s->currentOut = s->prevOut + ((in - s->prevOut) * s->invUpSlide); } else { s->currentOut = s->prevOut + ((in - s->prevOut) * s->invDownSlide); } if (s->currentOut < VSF) s->currentOut = 0.0f; s->prevIn = in; s->prevOut = s->currentOut; return s->currentOut; }