1 module PixelPerfectEngine.audio.common; 2 3 import PixelPerfectEngine.system.platform; 4 static if(USE_INTEL_INTRINSICS){ 5 import inteli.emmintrin; 6 //immutable __m128i zeroes; 7 immutable __m128 multiVal = [32_768f, 32_768f, 32_768f, 32_768f]; 8 }else{ 9 immutable float[4] multiVal = [32_768f, 32_768f, 32_768f, 32_768f]; 10 } 11 12 import std.bitmanip; 13 import std.math; 14 15 /** 16 * Defines basic functions for an envelope generator. 17 */ 18 public interface IEnvelopeGenerator{ 19 public @nogc uint updateEnvelopeState(); 20 public @nogc void setKeyOn(); 21 public @nogc void setKeyOff(); 22 } 23 /** 24 * Stores an MICP command on 64 bits. 25 */ 26 public struct MICPCommand{ 27 mixin(bitfields!( 28 ushort, "command", 6, 29 ushort, "channel", 10)); 30 ushort val0; /// 31 union{ 32 ushort[2] vals; 33 float valf; 34 ubyte[4] midiCMD; 35 uint val32; 36 } 37 /*alias note = val0; 38 alias parameter = val0; 39 alias velocity = val1[0]; 40 alias expr = val1[1]; 41 alias program = val1[0]; 42 alias bank = val1[1]; 43 alias valueInt = val1[0]; 44 alias valueFloat = val2;*/ 45 } 46 /** 47 * Command list for MICP 48 */ 49 public enum MICPCommandList : ushort{ 50 NULL = 0, 51 KeyOn = 1, ///val0: Note, val1: Velocity, val2: Expression 52 KeyOff = 2, ///val0: Note, val1: Velocity, val2: Expression 53 AfterTouch = 3, ///val0: Note, val1: Velocity, val2: Expression 54 PitchBend = 4, ///val0: Note, val1: Corase, val2: Fine 55 PitchBentFP = 5, ///val0: Note, valf: Amount 56 ProgSelect = 6, ///val0: Prog, val1: Bank if used, val2: Unused 57 ParamEdit = 7, ///val0: Parameter ID, val1: New value, val2: Unused 58 ParamEditFP = 8, ///val0: Parameter ID, valf: New value 59 ParamEdit32 = 9, ///val0: Parameter ID, val32: New value 60 PrgKeyOn = 11, ///Special key-on command. Same params as KeyOn. Mainly used for arpeggiation and programming sequences 61 PrgKeyOff = 12, ///Special key-off command. Same params as KeyOff. Mainly used for arpeggiation and programming sequences 62 SysExc = 16, 63 MIDIThruMICP = 17, ///val0 is unused 64 Wait = 24 ///val0: bits 32-47 if needed, val32: bits 0-31 65 } 66 67 /** 68 * All PPE-FX synths and effects should be inherited from this class. 69 */ 70 abstract class AbstractPPEFX{ 71 public abstract @nogc void render(float** inputBuffers, float** outputBuffers); 72 public abstract @nogc int setRenderParams(float samplerate, size_t framelength, size_t nOfFrames); 73 public abstract @nogc void receiveMICPCommand(MICPCommand cmd); 74 public abstract void loadConfig(ref void[] data); 75 public abstract ref void[] saveConfig(); 76 public abstract PPEFXInfo* getPPEFXInfo(); 77 /** 78 * Converts a midi note to frequency, can also take fine tuning. 79 */ 80 public @nogc float midiNoteToFrequency(float note, float tuning = 440.0f){ 81 return tuning * pow(2.0, (note - 69.0f) / 12.0f); 82 } 83 /** 84 * Changes the frequency by the given pitch. 85 */ 86 public @nogc float bendFreqByPitch(float pitch, float input){ 87 return input * pow(2.0, pitch / 12.0f); 88 } 89 public @nogc float frequencyToMidiNote(float frequency, float tuning = 440.0f){ 90 return 69.0f + 12.0f * log2(frequency / tuning); 91 } 92 /** 93 * Converts int16 values to single-precision floating-point. 94 */ 95 public @nogc void int16ToFloat(short* input, float* output, size_t length){ 96 static if(USE_INTEL_INTRINSICS){ 97 while(length > 4){ 98 __m128i vals;// = [input[0],input[1],input[2],input[3]]; 99 vals[0] = input[0]; 100 vals[1] = input[1]; 101 vals[2] = input[2]; 102 vals[3] = input[3]; 103 *cast(__m128*)output = _mm_cvtepi32_ps(vals) / multiVal; 104 input += 4; 105 output += 4; 106 length -= 4; 107 } 108 } 109 while(length > 0){ 110 *output = cast(float)*input / multiVal[0]; 111 input++; 112 output++; 113 length--; 114 } 115 } 116 /** 117 * Converts single-precision floating point to int16 values. 118 */ 119 public @nogc void floatToInt16(float* input, short* output, size_t length){ 120 static if(USE_INTEL_INTRINSICS){ 121 while(length > 4){ 122 __m128i vals = _mm_cvtps_epi32(*cast(__m128*)input * multiVal); 123 output[0] = cast(short)vals[0]; 124 output[1] = cast(short)vals[1]; 125 output[2] = cast(short)vals[2]; 126 output[3] = cast(short)vals[3]; 127 input += 4; 128 output += 4; 129 length -= 4; 130 } 131 } 132 while(length > 0){ 133 *output = cast(short)(*input / multiVal[0]); 134 input++; 135 output++; 136 length--; 137 } 138 } 139 /** 140 * Mixes a stream into the target. 141 */ 142 public @nogc void mixStreamIntoTarget(float* input, float* output, size_t length, float sendLevel){ 143 static if(USE_INTEL_INTRINSICS){ 144 __m128 sendLevel4;// = [sendLevel,sendLevel,sendLevel,sendLevel]; 145 sendLevel4[0] = sendLevel; 146 sendLevel4[1] = sendLevel; 147 sendLevel4[2] = sendLevel; 148 sendLevel4[3] = sendLevel; 149 while(length > 4){ 150 *cast(__m128*)output += *cast(__m128*)input * sendLevel4; 151 input += 4; 152 output += 4; 153 length -= 4; 154 } 155 } 156 while(length > 0){ 157 *output += *input * sendLevel; 158 input++; 159 output++; 160 length--; 161 } 162 } 163 /** 164 * Converts an int16 stream into floating point, then adds it to the target. 165 */ 166 public @nogc void convAndMixStreamIntoTarget(short* input, float* output, size_t length, float sendLevel){ 167 static if(USE_INTEL_INTRINSICS){ 168 __m128 sendLevel4;// = [sendLevel,sendLevel,sendLevel,sendLevel]; 169 sendLevel4[0] = sendLevel; 170 sendLevel4[1] = sendLevel; 171 sendLevel4[2] = sendLevel; 172 sendLevel4[3] = sendLevel; 173 while(length > 4){ 174 __m128i vals;// = [input[0],input[1],input[2],input[3]]; 175 vals[0] = input[0]; 176 vals[1] = input[1]; 177 vals[2] = input[2]; 178 vals[3] = input[3]; 179 *cast(__m128*)output += (_mm_cvtepi32_ps(vals) / multiVal) * sendLevel4; 180 input += 4; 181 output += 4; 182 length -= 4; 183 } 184 } 185 while(length > 0){ 186 *output += (cast(float)*input / multiVal[0]) * sendLevel; 187 input++; 188 output++; 189 length--; 190 } 191 } 192 } 193 194 public struct PPEFXInfo{ 195 public int nOfInputs; 196 public int nOfOutputs; 197 public string[] inputNames; 198 public string[] outputNames; 199 public bool isSynth; 200 201 }