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 
10 public enum StageType : ubyte{
11 	NULL		=	0,	/// Terminates the envelope
12 	ascending	=	1,
13 	descending	=	2,
14 	sustain		=	3,	/// Descending until either a key release command or reaches zero.
15 	revSustain	=	4,	/// Ascending until either key release or max level reached.
16 	hold		=	5,	/// Keeps the value of targetLevel until holdTime.
17 }
18 
19 /**
20  * Defines a single envelope stage.
21  */
22 public struct EnvelopeStage{
23 
24 	public uint targetLevel;	/// If reached, jumps to the next stage 
25 	public uint stepping;		/// If slow enabled, it sets how much clockcycles needed for a single increment, otherwise sets the increment for each clockcycle
26 	public byte linearity;		/// 0 = Linear. Greater than 0 = Pseudolog. Less than 0 = Pseudoantilog.
27 	mixin(bitfields!(
28 		ubyte, "type", 3,
29 		bool, "slow", 1,
30 		ubyte, "unused", 4));
31 	public ushort holdTime;		/// If used, it describes the hold time in milliseconds
32 }
33 
34 public struct EnvelopeGenerator{
35 	private EnvelopeStage[] stages;
36 	private sizediff_t currentStage;
37 	private uint currentLevel, stepNumber;
38 	private uint log;
39 	private bool isRunning, keyON;
40 
41 	public @nogc void step(){
42 		if(isRunning){
43 			switch(stages[currentStage].type){
44 				case StageType.ascending:
45 					if(stages[currentStage].linearity > 0){
46 						currentLevel += stages[currentStage].stepping;
47 						log += stages[currentStage].stepping;
48 						currentLevel += log / (128 - stages[currentStage].linearity);
49 						
50 						if(currentLevel >= stages[currentStage].targetLevel){
51 							currentLevel = stages[currentStage].targetLevel;
52 							currentStage++;
53 							log = 0;
54 						}
55 					}else if(stages[currentStage].linearity < 0){
56 						currentLevel += stages[currentStage].stepping;
57 						currentLevel += (stages[currentStage].targetLevel - currentLevel) / (129 - stages[currentStage].linearity * -1);
58 						if(currentLevel >= stages[currentStage].targetLevel){
59 							currentLevel = stages[currentStage].targetLevel;
60 							currentStage++;
61 						}
62 					}else{
63 						if(stages[currentStage].slow){
64 							stepNumber++;
65 							if(stepNumber == stages[currentStage].stepping){
66 								stepNumber = 0;
67 								currentLevel++;
68 							}
69 						}else{
70 							currentLevel += stages[currentStage].stepping;
71 						}
72 						if(currentLevel >= stages[currentStage].targetLevel){
73 							currentLevel = stages[currentStage].targetLevel;
74 							currentStage++;
75 						}
76 					}
77 					break;
78 				case StageType.descending:
79 					if(stages[currentStage].linearity > 0){
80 						currentLevel -= stages[currentStage].stepping;
81 						log += stages[currentStage].stepping;
82 						currentLevel -= log / (128 - stages[currentStage].linearity);
83 						
84 						if(currentLevel <= stages[currentStage].targetLevel){
85 							currentLevel = stages[currentStage].targetLevel;
86 							currentStage++;
87 							log = 0;
88 						}
89 					}else if(stages[currentStage].linearity < 0){
90 						currentLevel -= stages[currentStage].stepping;
91 						currentLevel -= (stages[currentStage].targetLevel - currentLevel) / (129 - stages[currentStage].linearity * -1);
92 						if(currentLevel <= stages[currentStage].targetLevel){
93 							currentLevel = stages[currentStage].targetLevel;
94 							currentStage++;
95 						}
96 					}else{
97 						if(stages[currentStage].slow){
98 							stepNumber++;
99 							if(stepNumber == stages[currentStage].stepping){
100 								stepNumber = 0;
101 								currentLevel--;
102 							}
103 						}else{
104 							currentLevel -= stages[currentStage].stepping;
105 						}
106 						if(currentLevel <= stages[currentStage].targetLevel){
107 							currentLevel = stages[currentStage].targetLevel;
108 							currentStage++;
109 						}
110 					}
111 					break;
112 				case StageType.sustain:
113 					if(stages[currentStage].linearity > 0){
114 						currentLevel -= stages[currentStage].stepping;
115 						log += stages[currentStage].stepping;
116 						currentLevel -= log / (128 - stages[currentStage].linearity);
117 						
118 						if(currentLevel <= 0){
119 							isRunning = false;
120 						}
121 						if(!keyON){
122 							currentStage++;
123 						}
124 					}else if(stages[currentStage].linearity < 0){
125 						currentLevel -= stages[currentStage].stepping;
126 						currentLevel -= (stages[currentStage].targetLevel - currentLevel) / (129 - stages[currentStage].linearity * -1);
127 						if(currentLevel <= 0){
128 							isRunning = false;
129 						}
130 						if(!keyON){
131 							currentStage++;
132 						}
133 					}else{
134 						if(stages[currentStage].slow){
135 							stepNumber++;
136 							if(stepNumber == stages[currentStage].stepping){
137 								stepNumber = 0;
138 								currentLevel--;
139 							}
140 						}else{
141 							currentLevel -= stages[currentStage].stepping;
142 						}
143 						if(currentLevel <= 0){
144 							isRunning = false;
145 						}
146 						if(!keyON){
147 							currentStage++;
148 						}
149 					}
150 					break;
151 				default:
152 					isRunning = false;
153 					break;
154 			}
155 		}
156 	}
157 }