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 }