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 }