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 }