Merge remote-tracking branch 'origin/master'
[zynaddsubfx-code.git] / src / Synth / Envelope.cpp
blob6280988b81a9993b0ce110caeb0a0f34d8a42d49
1 /*
2 ZynAddSubFX - a software synthesizer
4 Envelope.cpp - Envelope implementation
5 Copyright (C) 2002-2005 Nasca Octavian Paul
6 Author: Nasca Octavian Paul
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.
14 #include <cmath>
15 #include "Envelope.h"
16 #include "../Params/EnvelopeParams.h"
18 namespace zyn {
20 Envelope::Envelope(EnvelopeParams &pars, float basefreq, float bufferdt,
21 WatchManager *m, const char *watch_prefix)
22 :watchOut(m, watch_prefix, "out")
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;
32 if(!pars.Pfreemode)
33 pars.converttofree();
35 mode = pars.Envmode;
37 //for amplitude envelopes
38 if((mode == 1) && !linearenvelope)
39 mode = 2; //change to log envelope
40 if((mode == 2) && linearenvelope)
41 mode = 1; //change to linear
43 for(int i = 0; i < MAX_ENVELOPE_POINTS; ++i) {
44 const float tmp = pars.getdt(i) * envstretch;
45 if(tmp > bufferdt)
46 envdt[i] = bufferdt / tmp;
47 else
48 envdt[i] = 2.0f; //any value larger than 1
50 switch(mode) {
51 case 2:
52 envval[i] = (1.0f - pars.Penvval[i] / 127.0f) * -40;
53 break;
54 case 3:
55 envval[i] =
56 (powf(2, 6.0f
57 * fabsf(pars.Penvval[i]
58 - 64.0f) / 64.0f) - 1.0f) * 100.0f;
59 if(pars.Penvval[i] < 64)
60 envval[i] = -envval[i];
61 break;
62 case 4:
63 envval[i] = (pars.Penvval[i] - 64.0f) / 64.0f * 6.0f; //6 octaves (filtru)
64 break;
65 case 5:
66 envval[i] = (pars.Penvval[i] - 64.0f) / 64.0f * 10;
67 break;
68 default:
69 envval[i] = pars.Penvval[i] / 127.0f;
73 envdt[0] = 1.0f;
75 currentpoint = 1; //the envelope starts from 1
76 keyreleased = false;
77 t = 0.0f;
78 envfinish = false;
79 inct = envdt[1];
80 envoutval = 0.0f;
83 Envelope::~Envelope()
88 * Release the key (note envelope)
90 void Envelope::releasekey()
92 if(keyreleased)
93 return;
94 keyreleased = true;
95 if(forcedrelease)
96 t = 0.0f;
99 void Envelope::forceFinish(void)
101 envfinish = true;
104 void Envelope::watch(float time, float value)
106 float pos[2];
107 float factor1;
108 float factor2;
109 pos[0] = time;
110 switch(mode) {
111 case 2:
112 pos[1] = 1 - value / -40.f;
113 watchOut(pos, 2);
114 break;
115 case 3:
116 factor1 = log(value/100. + 1.) / (6. * log(2));
117 factor2 = log(1. - value/100.) / (6. * log(2));
118 pos[1] = ((0.5 * factor1) >= 0) ? (0.5 * factor1 + 0.5) : (0.5 - factor2 * 0.5);
119 watchOut(pos, 2);
120 break;
121 case 4:
122 pos[1] = (value + 6.) / 12.f;
123 watchOut(pos, 2);
124 break;
125 case 5:
126 pos[1] = (value + 10.) / 20.f;
127 watchOut(pos, 2);
128 break;
129 default:
130 pos[1] = value;
131 watchOut(pos, 2);
136 * Envelope Output
138 float Envelope::envout(bool doWatch)
140 float out;
141 if(envfinish) { //if the envelope is finished
142 envoutval = envval[envpoints - 1];
143 if(doWatch) {
144 watch(envpoints - 1, envoutval);
146 return envoutval;
148 if((currentpoint == envsustain + 1) && !keyreleased) { //if it is sustaining now
149 envoutval = envval[envsustain];
150 if(doWatch) {
151 watch(envsustain, envoutval);
153 return envoutval;
156 if(keyreleased && forcedrelease) { //do the forced release
157 int tmp = (envsustain < 0) ? (envpoints - 1) : (envsustain + 1); //if there is no sustain point, use the last point for release
159 if(envdt[tmp] < 0.00000001f)
160 out = envval[tmp];
161 else
162 out = envoutval + (envval[tmp] - envoutval) * t;
164 t += envdt[tmp] * envstretch;
166 if(t >= 1.0f) { // move to the next segment
167 currentpoint = envsustain + 2;
168 forcedrelease = 0;
169 t = 0.0f;
170 inct = envdt[currentpoint];
171 if((currentpoint >= envpoints) || (envsustain < 0))
172 envfinish = true;
175 if(doWatch) {
176 watch(tmp + t, envoutval);
179 return out;
181 if(inct >= 1.0f)
182 out = envval[currentpoint];
183 else
184 out = envval[currentpoint - 1]
185 + (envval[currentpoint] - envval[currentpoint - 1]) * t;
187 t += inct;
189 if(t >= 1.0f) {
190 if(currentpoint >= envpoints - 1)
191 envfinish = true;
192 else
193 currentpoint++;
194 t = 0.0f;
195 inct = envdt[currentpoint];
198 envoutval = out;
200 if(doWatch) {
201 watch(currentpoint + t, envoutval);
203 return out;
207 * Envelope Output (dB)
209 float Envelope::envout_dB()
211 float out;
212 if(linearenvelope)
213 return envout(true);
215 if((currentpoint == 1) && (!keyreleased || !forcedrelease)) { //first point is always lineary interpolated <- seems to have odd effects
216 float v1 = EnvelopeParams::env_dB2rap(envval[0]);
217 float v2 = EnvelopeParams::env_dB2rap(envval[1]);
218 out = v1 + (v2 - v1) * t;
220 t += inct;
222 if(t >= 1.0f) {
223 t = 0.0f;
224 inct = envdt[2];
225 currentpoint++;
226 out = v2;
229 if(out > 0.001f)
230 envoutval = EnvelopeParams::env_rap2dB(out);
231 else
232 envoutval = MIN_ENVELOPE_DB;
233 out = envoutval;
234 } else
235 out = envout(false);
237 watch(currentpoint + t, out);
238 return EnvelopeParams::env_dB2rap(out);
242 bool Envelope::finished() const
244 return envfinish;