Envelope.cpp (7338B)
1 /* 2 ZynAddSubFX - a software synthesizer 3 4 Envelope.cpp - Envelope implementation 5 Copyright (C) 2002-2005 Nasca Octavian Paul 6 Author: Nasca Octavian Paul 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License 10 as published by the Free Software Foundation; either version 2 11 of the License, or (at your option) any later version. 12 */ 13 14 #include <cmath> 15 #include "Envelope.h" 16 #include "../Params/EnvelopeParams.h" 17 18 namespace zyn { 19 20 Envelope::Envelope(EnvelopeParams &pars, float basefreq, float bufferdt, 21 WatchManager *m, const char *watch_prefix) 22 :watchOut(m, watch_prefix, "out") 23 { 24 envpoints = pars.Penvpoints; 25 if(envpoints > MAX_ENVELOPE_POINTS) 26 envpoints = MAX_ENVELOPE_POINTS; 27 envsustain = (pars.Penvsustain == 0) ? -1 : pars.Penvsustain; 28 forcedrelease = pars.Pforcedrelease; 29 envstretch = powf(440.0f / basefreq, pars.Penvstretch / 64.0f); 30 linearenvelope = pars.Plinearenvelope; 31 repeating = pars.Prepeating; 32 33 if(!pars.Pfreemode) 34 pars.converttofree(); 35 36 mode = pars.Envmode; 37 38 //for amplitude envelopes 39 if((mode == 1) && !linearenvelope) 40 mode = 2; //change to log envelope 41 if((mode == 2) && linearenvelope) 42 mode = 1; //change to linear 43 44 for(int i = 0; i < MAX_ENVELOPE_POINTS; ++i) { 45 const float dtstretched = pars.getdt(i) * envstretch; 46 if(dtstretched > bufferdt) 47 envdt[i] = bufferdt / dtstretched; 48 else 49 envdt[i] = 2.0f; //any value larger than 1 50 51 switch(mode) { 52 case 2: 53 envval[i] = (1.0f - pars.Penvval[i] / 127.0f) * -40; 54 break; 55 case 3: 56 envval[i] = 57 (powf(2, 6.0f 58 * fabsf(pars.Penvval[i] 59 - 64.0f) / 64.0f) - 1.0f) * 100.0f; 60 if(pars.Penvval[i] < 64) 61 envval[i] = -envval[i]; 62 break; 63 case 4: 64 envval[i] = (pars.Penvval[i] - 64.0f) / 64.0f * 6.0f; //6 octaves (filtru) 65 break; 66 case 5: 67 envval[i] = (pars.Penvval[i] - 64.0f) / 64.0f * 10; 68 break; 69 default: 70 envval[i] = pars.Penvval[i] / 127.0f; 71 } 72 } 73 74 envdt[0] = 1.0f; 75 76 currentpoint = 1; //the envelope starts from 1 77 keyreleased = false; 78 t = 0.0f; 79 envfinish = false; 80 inct = envdt[1]; 81 envoutval = 0.0f; 82 } 83 84 Envelope::~Envelope() 85 {} 86 87 88 /* 89 * Release the key (note envelope) 90 */ 91 void Envelope::releasekey() 92 { 93 if(keyreleased) 94 return; 95 keyreleased = true; 96 if(forcedrelease) 97 t = 0.0f; 98 } 99 100 void Envelope::forceFinish(void) 101 { 102 envfinish = true; 103 } 104 105 void Envelope::watch(float time, float value) 106 { 107 float pos[2]; 108 float factor1; 109 float factor2; 110 pos[0] = time; 111 switch(mode) { 112 case 2: 113 pos[1] = 1 - value / -40.f; 114 watchOut(pos, 2); 115 break; 116 case 3: 117 factor1 = log(value/100. + 1.) / (6. * log(2)); 118 factor2 = log(1. - value/100.) / (6. * log(2)); 119 pos[1] = ((0.5 * factor1) >= 0) ? (0.5 * factor1 + 0.5) : (0.5 - factor2 * 0.5); 120 watchOut(pos, 2); 121 break; 122 case 4: 123 pos[1] = (value + 6.) / 12.f; 124 watchOut(pos, 2); 125 break; 126 case 5: 127 pos[1] = (value + 10.) / 20.f; 128 watchOut(pos, 2); 129 break; 130 default: 131 pos[1] = value; 132 watchOut(pos, 2); 133 } 134 } 135 136 /* 137 * Envelope Output 138 */ 139 float Envelope::envout(bool doWatch) 140 { 141 float out; 142 if(envfinish) { //if the envelope is finished 143 envoutval = envval[envpoints - 1]; 144 if(doWatch) { 145 watch(envpoints - 1, envoutval); 146 } 147 return envoutval; 148 } 149 150 if((currentpoint == envsustain + 1) && !keyreleased) { //if it is sustaining now 151 envoutval = envval[envsustain]; 152 bool zerorelease = true; 153 for (auto i = envsustain; i<envpoints; i++) 154 if (envval[i] != -40.0f) zerorelease = false; 155 if (zerorelease && //if sustaining at zero with zero until env ends 156 (mode == ADSR_lin || mode == ADSR_dB)) { // and its an amp envelope 157 envfinish = true; // finish voice to free resources 158 } 159 if(doWatch) { 160 watch(envsustain, envoutval); 161 } 162 return envoutval; 163 } 164 165 if(keyreleased && forcedrelease) { //do the forced release 166 int releaseindex = (envsustain < 0) ? (envpoints - 1) : (envsustain + 1); //if there is no sustain point, use the last point for release 167 168 if(envdt[releaseindex] < 0.00000001f) 169 out = envval[releaseindex]; 170 else 171 out = envoutval + (envval[releaseindex] - envoutval) * t; // linear interpolation envoutval and envval[releaseindex] 172 173 t += envdt[releaseindex]; 174 175 if(t >= 1.0f) { // move to the next segment 176 currentpoint = envsustain + 2; 177 forcedrelease = 0; 178 t = 0.0f; 179 inct = envdt[currentpoint]; 180 if((currentpoint >= envpoints) || (envsustain < 0)) 181 envfinish = true; 182 } 183 184 if(doWatch) { 185 watch(releaseindex + t, envoutval); 186 } 187 188 return out; 189 } 190 if(inct >= 1.0f) 191 out = envval[currentpoint]; 192 else 193 out = envval[currentpoint - 1] 194 + (envval[currentpoint] - envval[currentpoint - 1]) * t; 195 196 t += inct; 197 198 if(t >= 1.0f) { 199 if(currentpoint >= envpoints - 1) // if last point reached 200 envfinish = true; 201 // but if reached sustain point, repeating activated and key still pressed or sustained 202 else if (repeating && currentpoint == envsustain && !keyreleased) { 203 // set first value to sustain value to prevent jump 204 envval[0] = envval[currentpoint]; 205 // reset current point 206 currentpoint = 1; 207 } 208 // otherwise proceed to the next segment 209 else currentpoint++; 210 211 t = 0.0f; 212 inct = envdt[currentpoint]; 213 } 214 215 envoutval = out; 216 217 if(doWatch) { 218 watch(currentpoint + t, envoutval); 219 } 220 return out; 221 } 222 223 /* 224 * Envelope Output (dB) 225 */ 226 float Envelope::envout_dB() 227 { 228 float out; 229 if(linearenvelope) 230 return envout(true); 231 232 if((currentpoint == 1) && (!keyreleased || !forcedrelease)) { //first point is always lineary interpolated <- seems to have odd effects 233 float v1 = EnvelopeParams::env_dB2rap(envval[0]); 234 float v2 = EnvelopeParams::env_dB2rap(envval[1]); 235 out = v1 + (v2 - v1) * t; 236 237 t += inct; 238 239 if(t >= 1.0f) { 240 t = 0.0f; 241 inct = envdt[2]; 242 currentpoint++; 243 out = v2; 244 } 245 246 if(out > 0.001f) 247 envoutval = EnvelopeParams::env_rap2dB(out); 248 else 249 envoutval = MIN_ENVELOPE_DB; 250 out = envoutval; 251 } else 252 out = envout(false); 253 254 watch(currentpoint + t, out); 255 return EnvelopeParams::env_dB2rap(out); 256 257 } 258 259 bool Envelope::finished() const 260 { 261 return envfinish; 262 } 263 264 }