1 module pixelperfectengine.audio.base.osc; 2 3 import pixelperfectengine.system.etc : clamp; 4 import inteli.emmintrin; 5 6 /** 7 * An oscillator that generates multiple waveform outputs from a single counter, and allows the mixing of them. 8 * Available waveforms: 9 * * sawtooth: The basic output of the counter. 10 * * triangle: Produced from the counter by using the top-bit of the counter to invert the next 16 bits. 11 * * pulse: Produced from the counter with a simple logic of testing the counter against the `pulseWidth` variable. 12 * * sawpulse: Sawtooth and pulse logically AND-ed together 13 * The amounts can be set to minus, this way the output will be inverted. 14 */ 15 public struct MultiTapOsc { 16 ///The rate of which the oscillator operates at 17 protected uint rate; 18 ///Current state of the oscillator 19 protected uint counter; 20 ///Controls the pulse width of the oscillator 21 public uint pulseWidth; 22 ///Controls the amount of the sawtooth wave (minus means inverting) 23 public short sawAm; 24 ///Controls the amount of the triangle wave (minus means inverting) 25 public short triAm; 26 ///Controls the amount of the pulse wave (minus means inverting) 27 public short pulseAm; 28 ///Controls the amount of the sawpulse wave (minus means inverting) 29 public short sawPulseAm; 30 /** 31 * Returns the integer output of the oscillator. 32 */ 33 int outputI() @nogc @safe pure nothrow { 34 const int pulseOut = (counter >= pulseWidth ? ushort.max : ushort.min); 35 const int sawOut = (counter >> 16); 36 const int triOut = (counter >> 15 ^ (sawOut & 0x80_00 ? ushort.max : ushort.min) & ushort.max); 37 counter += rate; 38 return (((pulseOut + short.min) * pulseAm)>>15) + (((sawOut + short.min) * pulseAm)>>15) + 39 (((triOut + short.min) * triAm)>>15) + ((((sawOut & pulseOut) + short.min) * sawPulseAm)>>15); 40 } 41 /** 42 * Returns the floating point output of the oscillator 43 * Params: 44 * bias = Offset of the output. 45 * invDiv = Inverse divident to convert to a floating-point scale. Equals with 1/divident. 46 */ 47 double outputF(double bias, double invDiv) @nogc @safe pure nothrow { 48 return (outputI() * invDiv) + bias; 49 } 50 /** 51 * Sets the rate of the oscillator 52 * Params: 53 * sampleRate = The current sampling frequency. 54 * freq = The frequency to set the oscillator. 55 */ 56 void setRate(int sampleRate, double freq) @nogc @safe pure nothrow { 57 double rateD = freq / (sampleRate / cast(double)(1<<16)); 58 rate = cast(uint)(cast(double)(1<<16) * rateD); 59 } 60 void reset() @nogc @safe pure nothrow { 61 counter = 0; 62 } 63 } 64 /** 65 * An oscillator that generates multiple waveforms from 4 separate counters, and allows the mixing of them, with 4 possible 66 * outputs. 67 * Available waveforms: 68 * * sawtooth: The basic output of the counter. 69 * * triangle: Produced from the counter by using the top-bit of the counter to invert the next 16 bits. 70 * * pulse: Produced from the counter with a simple logic of testing the counter against the `pulseWidth` variable. 71 * * sawpulse: Sawtooth and pulse logically AND-ed together 72 * The amounts can be set to minus, this way the output will be inverted. 73 */ 74 public struct QuadMultitapOsc { 75 __m128i rate; 76 __m128i counter; 77 __m128i pulseWidth; 78 __m128i syncReset; ///Used for sync resets 79 short8 levelCtrl01; ///Even number elements set the saw, odd number elements set the triangle amount 80 short8 levelCtrl23; ///Even number elements set the pulse, odd number elements set the sawpulse amount 81 static immutable __m128i triTest = __m128i(int.max); 82 static immutable short8 wordOffset = short8(short.min); 83 ///Returns the output and forwards the oscillator by a single step. 84 __m128i output() @nogc @safe pure nothrow { 85 __m128i result; 86 const __m128i pulseOut = _mm_cmpgt_epi32(counter, pulseWidth); 87 const __m128i triOut = _mm_slli_epi32(_mm_cmpgt_epi32(counter, triTest) ^ counter, 1); 88 const __m128i spOut = pulseOut & counter; 89 const __m128i out01 = _mm_sub_epi16(_mm_packs_epi32(_mm_srai_epi32(counter, 16), _mm_srai_epi32(triOut, 16)), 90 cast(__m128i)wordOffset); 91 const __m128i out23 = _mm_sub_epi16(_mm_packs_epi32(_mm_srai_epi32(pulseOut, 16), _mm_srai_epi32(spOut, 16)), 92 cast(__m128i)wordOffset); 93 result = _mm_madd_epi16(out01, cast(__m128i)levelCtrl01) + _mm_madd_epi16(out23, cast(__m128i)levelCtrl23); 94 counter += rate; 95 return result; 96 } 97 /** 98 * Implements hard synchronization between the first and any other oscillators. 99 * Template params: 100 * osc = the oscillator selection. 101 * Note: Hardsync causes noticeable aliasing artifacts on the output. 102 */ 103 __m128i outputHSync0(int[] osc)() @nogc @safe pure nothrow { 104 const int prevState = counter[0]; 105 __m128i result = output(); 106 if (prevState > counter[0]) { 107 static foreach (i ; osc) { 108 counter[i] = 0; 109 } 110 } 111 return result; 112 } 113 /** 114 * Implements soft synchronization between the first and any other oscillators. 115 * Template params: 116 * osc = the oscillator selection. 117 */ 118 __m128i outputSSync0(int[] osc)() @nogc @safe pure nothrow { 119 const int prevState = counter[0]; 120 __m128i result = output(); 121 if (prevState > counter[0]) { 122 static foreach (i ; osc) { 123 counter[i] = syncReset[i] * counter[0]; 124 } 125 } 126 return result; 127 } 128 /** 129 * Sets the rate of a given oscillator. 130 * Params: 131 * sampleRate = Sampling frequency. 132 * freq = The desired output frequency. 133 * osc = Number of the oscillator to be set. 134 */ 135 void setRate(int sampleRate, double freq, int osc) @nogc @safe pure nothrow { 136 double cycLen = freq / (sampleRate / cast(double)(1<<16)); 137 rate[osc] = cast(uint)(cast(double)(1<<16) * cycLen); 138 syncReset[osc] = cast(uint)(cast(double)rate[0] / (cast(double)(1<<16) * cycLen)); 139 } 140 }