1 module PixelPerfectEngine.audio.envGen;
2 /*
3  * Copyright (C) 2015-2018, by Laszlo Szeremi under the Boost license.
4  *
5  * Pixel Perfect Engine, envelop generator module
6  */
7 
8 import std.bitmanip;
9 import std.container.array;
10 
11 public enum StageType : ubyte{
12 	NULL		=	0,	/// Terminates the envelope
13 	ascending	=	1,
14 	descending	=	2,
15 	sustain		=	3,	/// Descending until either a key release command or reaches zero.
16 	revSustain	=	4,	/// Ascending until either key release or max level reached.
17 	hold		=	5,	/// Keeps the value of targetLevel until holdTime.
18 	release		=	7,	/// Used on key-release,
19 }
20 
21 /**
22  * Defines a single envelope stage.
23  */
24 public struct EnvelopeStage{
25 
26 	public int targetLevel;	/// If reached, jumps to the next stage
27 	public int stepping;		/// If slow enabled, it sets how much cycles needed for a single increment, otherwise sets the increment for each cycle
28 	public byte linearity;		/// 0 = Linear. Greater than 0 = Pseudolog. Less than 0 = Pseudoantilog.
29 	mixin(bitfields!(
30 		ubyte, "type", 3,
31 		bool, "slow", 1,
32 		ubyte, "unused", 4));
33 	public ushort holdTime;		/// If used, it describes the hold time in milliseconds
34 }
35 ///to fix some readability
36 alias EnvelopeStageList = Array!(EnvelopeStage);
37 /**
38  * n-stage envelope generator with high-programmability and nonlinear capabilities.
39  */
40 public struct EnvelopeGenerator{
41 	public EnvelopeStageList* stages;
42 	private sizediff_t currentStage;
43 	private int currentLevel, stepNumber;
44 	private int log;
45 	private bool isRunning, keyON, keyRelease;
46 
47 	public @nogc @property int output(){
48 		return currentLevel;
49 	}
50 	public @nogc void reset(){
51 		currentStage = 0;
52 		currentLevel = 0;
53 		stepNumber = 0;
54 		log = 0;
55 		isRunning = false;
56 		keyON = false;
57 	}
58 	public @nogc void setKeyOn(){
59 		keyON = true;
60 		isRunning = true;
61 	}
62 	public @nogc void setKeyOff(){
63 		keyON = false;
64 	}
65 	public @nogc void step(){
66 		if(isRunning){
67 			if(keyON){
68 				switch(stages[0][currentStage].type){
69 					case StageType.ascending:
70 						if(stages[0][currentStage].linearity > 0){
71 							currentLevel += stages[0][currentStage].stepping;
72 							log += stages[0][currentStage].stepping;
73 							currentLevel += log / (128 - stages[0][currentStage].linearity);
74 
75 							if(currentLevel >= stages[0][currentStage].targetLevel){
76 								currentLevel = stages[0][currentStage].targetLevel;
77 								currentStage++;
78 								log = 0;
79 							}
80 						}else if(stages[0][currentStage].linearity < 0){
81 							currentLevel += stages[0][currentStage].stepping;
82 							currentLevel += (stages[0][currentStage].targetLevel - currentLevel) / (129 - stages[0][currentStage].linearity * -1);
83 							if(currentLevel >= stages[0][currentStage].targetLevel){
84 								currentLevel = stages[0][currentStage].targetLevel;
85 								currentStage++;
86 							}
87 						}else{
88 							if(stages[0][currentStage].slow){
89 								stepNumber++;
90 								if(stepNumber == stages[0][currentStage].stepping){
91 									stepNumber = 0;
92 									currentLevel++;
93 								}
94 							}else{
95 								currentLevel += stages[0][currentStage].stepping;
96 							}
97 							if(currentLevel >= stages[0][currentStage].targetLevel){
98 								currentLevel = stages[0][currentStage].targetLevel;
99 								currentStage++;
100 							}
101 						}
102 						break;
103 					case StageType.descending:
104 						if(stages[0][currentStage].linearity > 0){
105 							currentLevel -= stages[0][currentStage].stepping;
106 							log += stages[0][currentStage].stepping;
107 							currentLevel -= log / (128 - stages[0][currentStage].linearity);
108 
109 							if(currentLevel <= stages[0][currentStage].targetLevel){
110 								currentLevel = stages[0][currentStage].targetLevel;
111 								currentStage++;
112 								log = 0;
113 							}
114 						}else if(stages[0][currentStage].linearity < 0){
115 							currentLevel -= stages[0][currentStage].stepping;
116 							currentLevel -= (stages[0][currentStage].targetLevel - currentLevel) / (129 - stages[0][currentStage].linearity * -1);
117 							if(currentLevel <= stages[0][currentStage].targetLevel){
118 								currentLevel = stages[0][currentStage].targetLevel;
119 								currentStage++;
120 							}
121 						}else{
122 							if(stages[0][currentStage].slow){
123 								stepNumber++;
124 								if(stepNumber >= stages[0][currentStage].stepping){
125 									stepNumber = 0;
126 									currentLevel--;
127 								}
128 							}else{
129 								currentLevel -= stages[0][currentStage].stepping;
130 							}
131 							if(currentLevel <= stages[0][currentStage].targetLevel){
132 								currentLevel = stages[0][currentStage].targetLevel;
133 								currentStage++;
134 							}
135 						}
136 						break;
137 					case StageType.sustain:
138 						if(stages[0][currentStage].linearity > 0){
139 							currentLevel -= stages[0][currentStage].stepping;
140 							log += stages[0][currentStage].stepping;
141 							currentLevel -= log / (128 - stages[0][currentStage].linearity);
142 
143 							if(currentLevel <= 0){
144 								isRunning = false;
145 							}
146 							if(!keyON){
147 								currentStage++;
148 							}
149 						}else if(stages[0][currentStage].linearity < 0){
150 							currentLevel -= stages[0][currentStage].stepping;
151 							currentLevel -= (stages[0][currentStage].targetLevel - currentLevel) / (129 - stages[0][currentStage].linearity * -1);
152 							if(currentLevel <= 0){
153 								isRunning = false;
154 							}
155 							if(!keyON){
156 								currentStage++;
157 							}
158 						}else{
159 							if(stages[0][currentStage].slow){
160 								stepNumber++;
161 								if(stepNumber >= stages[0][currentStage].stepping){
162 									stepNumber = 0;
163 									currentLevel--;
164 								}
165 							}else{
166 								currentLevel -= stages[0][currentStage].stepping;
167 							}
168 							if(currentLevel <= 0){
169 								isRunning = false;
170 							}
171 							if(!keyON){
172 								currentStage++;
173 							}
174 						}
175 						break;
176 					default:
177 						isRunning = false;
178 						break;
179 				}
180 			}else{		//keyOFF
181 				if(keyRelease){
182 					//descending, but terminate when reaching zero
183 					if(stages[0][currentStage].linearity > 0){
184 						currentLevel -= stages[0][currentStage].stepping;
185 						log += stages[0][currentStage].stepping;
186 						currentLevel -= log / (128 - stages[0][currentStage].linearity);
187 						if(currentLevel <= 0){
188 							currentLevel = 0;
189 							reset;
190 							log = 0;
191 						}
192 					}else if(stages[0][currentStage].linearity < 0){
193 						currentLevel -= stages[0][currentStage].stepping;
194 						currentLevel -= currentLevel / (129 - stages[0][currentStage].linearity * -1);
195 						if(currentLevel <= 0){
196 							currentLevel = 0;
197 							reset;
198 						}
199 					}else{
200 						if(stages[0][currentStage].slow){
201 							stepNumber++;
202 							if(stepNumber >= stages[0][currentStage].stepping){
203 								stepNumber = 0;
204 								currentLevel--;
205 							}
206 						}else{
207 							currentLevel -= stages[0][currentStage].stepping;
208 						}
209 						if(currentLevel <= 0){
210 							currentLevel = 0;
211 							reset;
212 						}
213 					}
214 				}else{
215 					//find a key release stage, if not present then terminate session by resetting
216 					for(; currentStage < stages.length ; currentStage++){
217 						if(stages[0][currentStage].type == StageType.release){
218 							keyRelease = true;
219 						}else if(stages[0][currentStage].type == StageType.NULL){
220 							break;
221 						}
222 					}
223 					reset;
224 				}
225 			}
226 		}
227 	}
228 }