1 module pixelperfectengine.audio.base.osc;
2 
3 import pixelperfectengine.system.etc : clamp;
4 /** 
5  * Implements an oscillator base.
6  */
7 public abstract class Oscillator {
8 	protected float		_frequency;		///The current frequency of the oscillator
9 	protected int		_slmpFreq;		///Sampling frequency of the output
10 	/** 
11 	 * Returns the sampling frequency set in this oscillator.
12 	 */
13 	public int slmpFreq() @nogc @safe pure nothrow const {
14 		return _slmpFreq;
15 	}
16 	/** 
17 	 * Sets the sampling frequency of the oscillator.
18 	 * In devived classes, this might also change the internal state of the oscillator.
19 	 */
20 	public int slmpFreq(int val) @nogc @safe pure nothrow {
21 		return _slmpFreq = val;
22 	}
23 	/** 
24 	 * Returns the current output frequency of the oscillator.
25 	 */
26 	public float frequency() @nogc @safe pure nothrow const {
27 		return _frequency;
28 	}
29 	/**
30 	 * Sets the new frequency of the oscillator.
31 	 * In devived classes, this might also change the internal state of the oscillator.
32 	 */
33 	public float frequency(float val) @nogc @safe pure nothrow {
34 		return _frequency = val;
35 	}
36 
37 	/**
38 	 * Generates an output based on the oscillator's internal states.
39 	 */
40 	public abstract void generate(float[] output) @nogc pure nothrow;
41 }
42 /** 
43  * Generates a pulse signal. If set to 0.5, the output will be a perfect square wave.
44  */
45 public class PulseGen : Oscillator {
46 	protected float			stepRate = 1;
47 	protected float			position = 0;
48 	public float			pulseWidth = 0.5;
49 	public this(int _slmpFreq, float _frequency, float pulseWidth) {
50 		this._slmpFreq = _slmpFreq;
51 		frequency = _frequency;
52 		this.pulseWidth = pulseWidth;
53 	}
54 	public override float frequency(float val) {
55 		_frequency = val;
56 		stepRate = (1 / _frequency) / _slmpFreq;
57 		return _frequency;
58 	}
59 	public override int slmpFreq(int val) @nogc @safe pure nothrow {
60 		_slmpFreq = val;
61 		stepRate = (1 / _frequency) / _slmpFreq;
62 		return _slmpFreq;
63 	}
64 	/**
65 	 * Generates an output based on the oscillator's internal states.
66 	 */
67 	public override void generate(float[] output) @nogc pure nothrow {
68 		for (size_t i ; i < output.length ; i++) {
69 			output[i] = position > pulseWidth ? -1 : 1;
70 			position += stepRate;
71 			position = position >= 1 ? 0 : position;
72 		}
73 	}
74 }
75 /** 
76  * Generates a variable triangle signal. If shape is set to 0.5, a regular triangle wave will be generated. 0 and 1 
77  * will generate regular saw waves. Any inbetween values generate asymmetric triangle waves.
78  */
79 public class TriangleGen : Oscillator {
80 	protected float			stepRate = 1;
81 	protected float			position = 0;
82 	protected float			slopeUp = 2;
83 	protected float			slopeDown = -2;
84 	protected float			state = 0;
85 	public float			shape = 0.5;
86 	public this(int _slmpFreq, float _frequency, float shape) {
87 		this._slmpFreq = _slmpFreq;
88 		frequency = _frequency;
89 		this.shape = shape;
90 		recalc();
91 	}
92 	protected final void recalc() @nogc @safe pure nothrow {
93 		stepRate = (1 / _frequency) / _slmpFreq;
94 		slopeUp = stepRate * 2 * shape;
95 		slopeDown = stepRate * -2 * (1 - shape);
96 	}
97 	public override float frequency(float val) {
98 		_frequency = val;
99 		stepRate = (1 / val) / _slmpFreq;
100 		recalc();
101 		return _frequency;
102 	}
103 	public override void generate(float[] output) @nogc pure nothrow {
104 		for (size_t i ; i < output.length ; i++) {
105 			state += position > shape ? slopeDown : slopeUp;
106 			clamp(state, -1, 1);
107 			output[i] = state;
108 			position += stepRate;
109 			position = position >= 1 ? 0 : position;
110 		}
111 	}
112 }