1 /*
2  * Copyright (c) 2019 Christian Schoenebeck
3  *
4  * http://www.linuxsampler.org
5  *
6  * This file is part of LinuxSampler and released under the same terms.
7  * See README file for details.
8  */
9 
10 #ifndef LS_LFO_CLUSTER_H
11 #define LS_LFO_CLUSTER_H
12 
13 #include "LFOAll.h"
14 #include <type_traits> // for std::conditional
15 
16 namespace LinuxSampler {
17 
18 /** @brief Low Frequency Oscillator (sampler internal template class).
19  *
20  * This is a generalized cluster class providing all our LFO implementations
21  * encapsulated as one single class. This is thus a C++ template variant of the
22  * similar public API C++ class LFO. Even though LFOCluster and LFO serve a
23  * similar purpose, you should however always use this template variant instead
24  * of the public API LFO class for sampler internal code. LFOCluster has a
25  * higher potential for the compiler to optimize the finally emitted
26  * instructions, however it requires direct access to our individual LFO
27  * implementations' code (which are subject to change at any time for
28  * performance reasons). Hence this template class is not suitable for public
29  * API purposes (that is for third party apps), hence the reason for the
30  + existence of the separate public API LFO class. The latter has the priority
31  * for API & ABI stability for the price of slightly reduced runtime efficiency
32  * though.
33  */
34 template<LFO::range_type_t RANGE>
35 class LFOCluster {
36 public:
37     uint8_t& ExtController = sine.ExtController; /// redirect to union
38 
39     /**
40      * Constructor
41      *
42      * @param Max - maximum value of the output levels
43      */
LFOCluster(float Max)44     LFOCluster(float Max) :
45         wave(LFO::wave_sine),
46         sine(Max) // union-like class: use any union member's constructor (one for all, all for one)
47     {
48     }
49 
50     /**
51      * Calculates exactly one sample point of the LFO wave.
52      *
53      * @returns next LFO level
54      */
render()55     inline float render() {
56         switch (wave) {
57             case LFO::wave_sine:      return sine.render();
58             case LFO::wave_triangle:  return triangle.render();
59             case LFO::wave_saw:       return saw.render();
60             case LFO::wave_square:    return square.render();
61         }
62         return 0.f;
63     }
64 
65     /**
66      * Will be called by the voice when the key / voice was triggered.
67      *
68      * @param Wave            - wave form to be used (e.g. sine, saw, square)
69      * @param Frequency       - frequency of the oscillator in Hz
70      * @param Phase           - phase displacement of wave form's start level
71      *                          (0°..360°)
72      * @param StartLevel      - on which level the wave should start
73      * @param InternalDepth   - firm, internal oscillator amplitude
74      * @param ExtControlDepth - defines how strong the external MIDI
75      *                          controller has influence on the
76      *                          oscillator amplitude
77      * @param FlipPhase       - inverts the oscillator wave against
78      *                          a horizontal axis
79      * @param SampleRate      - current sample rate of the engines
80      *                          audio output signal
81      */
trigger(LFO::wave_t Wave,float Frequency,float Phase,LFO::start_level_t StartLevel,uint16_t InternalDepth,uint16_t ExtControlDepth,bool FlipPhase,unsigned int SampleRate)82     void trigger(LFO::wave_t Wave, float Frequency, float Phase, LFO::start_level_t StartLevel, uint16_t InternalDepth, uint16_t ExtControlDepth, bool FlipPhase, unsigned int SampleRate) {
83         wave = Wave;
84         switch (Wave) {
85             case LFO::wave_sine:
86                 sine.trigger(Frequency, StartLevel, InternalDepth, ExtControlDepth, FlipPhase, SampleRate);
87                 sine.setPhase(Phase);
88                 break;
89             case LFO::wave_triangle:
90                 triangle.trigger(Frequency, StartLevel, InternalDepth, ExtControlDepth, FlipPhase, SampleRate);
91                 triangle.setPhase(Phase);
92                 break;
93             case LFO::wave_saw:
94                 saw.trigger(Frequency, StartLevel, InternalDepth, ExtControlDepth, FlipPhase, SampleRate);
95                 saw.setPhase(Phase);
96                 break;
97             case LFO::wave_square:
98                 square.trigger(Frequency, StartLevel, InternalDepth, ExtControlDepth, FlipPhase, SampleRate);
99                 square.setPhase(Phase);
100                 break;
101         }
102     }
103 
104     /**
105      * Update LFO depth with a new external controller value.
106      *
107      * @param ExtControlValue - new external controller value
108      */
updateByMIDICtrlValue(const uint16_t & ExtControlValue)109     void updateByMIDICtrlValue(const uint16_t& ExtControlValue) {
110         switch (wave) {
111             case LFO::wave_sine:
112                 sine.updateByMIDICtrlValue(ExtControlValue);
113                 break;
114             case LFO::wave_triangle:
115                 triangle.updateByMIDICtrlValue(ExtControlValue);
116                 break;
117             case LFO::wave_saw:
118                 saw.updateByMIDICtrlValue(ExtControlValue);
119                 break;
120             case LFO::wave_square:
121                 square.updateByMIDICtrlValue(ExtControlValue);
122                 break;
123         }
124     }
125 
126     /**
127      * Should be invoked after the LFO is triggered.
128      * @param phase From 0 to 360 degrees.
129      */
setPhase(float phase)130     void setPhase(float phase) {
131         switch (wave) {
132             case LFO::wave_sine:
133                 sine.setPhase(phase);
134                 break;
135             case LFO::wave_triangle:
136                 triangle.setPhase(phase);
137                 break;
138             case LFO::wave_saw:
139                 saw.setPhase(phase);
140                 break;
141             case LFO::wave_square:
142                 square.setPhase(phase);
143                 break;
144         }
145     }
146 
setFrequency(float Frequency,unsigned int SampleRate)147     void setFrequency(float Frequency, unsigned int SampleRate) {
148         switch (wave) {
149             case LFO::wave_sine:
150                 sine.setFrequency(Frequency, SampleRate);
151                 break;
152             case LFO::wave_triangle:
153                 triangle.setFrequency(Frequency, SampleRate);
154                 break;
155             case LFO::wave_saw:
156                 saw.setFrequency(Frequency, SampleRate);
157                 break;
158             case LFO::wave_square:
159                 square.setFrequency(Frequency, SampleRate);
160                 break;
161         }
162     }
163 
setScriptDepthFactor(float factor,bool isFinal)164     void setScriptDepthFactor(float factor, bool isFinal) {
165         switch (wave) {
166             case LFO::wave_sine:
167                 sine.setScriptDepthFactor(factor, isFinal);
168                 break;
169             case LFO::wave_triangle:
170                 triangle.setScriptDepthFactor(factor, isFinal);
171                 break;
172             case LFO::wave_saw:
173                 saw.setScriptDepthFactor(factor, isFinal);
174                 break;
175             case LFO::wave_square:
176                 square.setScriptDepthFactor(factor, isFinal);
177                 break;
178         }
179     }
180 
setScriptFrequencyFactor(float factor,unsigned int samplerate)181     void setScriptFrequencyFactor(float factor, unsigned int samplerate) {
182         switch (wave) {
183             case LFO::wave_sine:
184                 sine.setScriptFrequencyFactor(factor, samplerate);
185                 break;
186             case LFO::wave_triangle:
187                 triangle.setScriptFrequencyFactor(factor, samplerate);
188                 break;
189             case LFO::wave_saw:
190                 saw.setScriptFrequencyFactor(factor, samplerate);
191                 break;
192             case LFO::wave_square:
193                 square.setScriptFrequencyFactor(factor, samplerate);
194                 break;
195         }
196     }
197 
setScriptFrequencyFinal(float hz,unsigned int samplerate)198     void setScriptFrequencyFinal(float hz, unsigned int samplerate) {
199         switch (wave) {
200             case LFO::wave_sine:
201                 sine.setScriptFrequencyFinal(hz, samplerate);
202                 break;
203             case LFO::wave_triangle:
204                 triangle.setScriptFrequencyFinal(hz, samplerate);
205                 break;
206             case LFO::wave_saw:
207                 saw.setScriptFrequencyFinal(hz, samplerate);
208                 break;
209             case LFO::wave_square:
210                 square.setScriptFrequencyFinal(hz, samplerate);
211                 break;
212         }
213     }
214 
215 protected:
216     typedef LFOSineNumericComplexNr<RANGE> Sine;
217     typedef typename std::conditional<RANGE == LFO::range_signed, LFOTriangleSigned, LFOTriangleUnsigned>::type Triangle;
218     typedef LFOSawIntMathNew<RANGE> Saw;
219     typedef LFOSquareIntMath<RANGE> Square;
220 
221 private:
222     LFO::wave_t wave;
223     union {
224         Sine      sine;
225         Triangle  triangle;
226         Saw       saw;
227         Square    square;
228     };
229 };
230 
231 typedef LFOCluster<LFO::range_signed> LFOClusterSigned;
232 typedef LFOCluster<LFO::range_unsigned> LFOClusterUnsigned;
233 
234 } // namespace LinuxSampler
235 
236 #endif // LS_LFO_CLUSTER_H
237