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_LFOSINE_NUMERIC_COMPLEX_NR_H
11 #define LS_LFOSINE_NUMERIC_COMPLEX_NR_H
12 
13 #include "LFOBase.h"
14 
15 namespace LinuxSampler {
16 
17     /** @brief Sine LFO (numeric, complex nr implementation)
18      *
19      * This is a Sinus Low Frequency Oscillator implementation using a complex
20      * number with numeric (descrete) math as basis for its oscillation.
21      */
22     template<LFO::range_type_t RANGE>
23     class LFOSineNumericComplexNr : public LFOBase<RANGE> {
24         public:
25 
26             /**
27              * Constructor
28              *
29              * @param Max - maximum value of the output levels
30              */
LFOSineNumericComplexNr(float Max)31             LFOSineNumericComplexNr(float Max) : LFOBase<RANGE>::LFOBase(Max) {
32                 //NOTE: DO NOT add any custom initialization here, since it would break LFOCluster construction !
33             }
34 
35             /**
36              * Calculates exactly one sample point of the LFO wave.
37              *
38              * @returns next LFO level
39              */
render()40             inline float render() {
41                 real -= c * imag;
42                 imag += c * real;
43                 if (RANGE == LFO::range_unsigned)
44                     return real * normalizer + offset;
45                 else /* signed range */
46                     return real * normalizer;
47             }
48 
49             /**
50              * Update LFO depth with a new external controller value.
51              *
52              * @param ExtControlValue - new external controller value
53              */
updateByMIDICtrlValue(const uint16_t & ExtControlValue)54             inline void updateByMIDICtrlValue(const uint16_t& ExtControlValue) {
55                 this->ExtControlValue = ExtControlValue;
56 
57                 const float max = (this->InternalDepth + ExtControlValue * this->ExtControlDepthCoeff) * this->ScriptDepthFactor;
58                 if (RANGE == LFO::range_unsigned) {
59                     normalizer = max * 0.5f;
60                     offset     = normalizer;
61                 } else { // signed range
62                     normalizer = max;
63                 }
64             }
65 
66             /**
67              * Will be called by the voice when the key / voice was triggered.
68              *
69              * @param Frequency       - frequency of the oscillator in Hz
70              * @param StartLevel      - on which level the wave should start
71              * @param InternalDepth   - firm, internal oscillator amplitude
72              * @param ExtControlDepth - defines how strong the external MIDI
73              *                          controller has influence on the
74              *                          oscillator amplitude
75              * @param FlipPhase       - inverts the oscillator wave against
76              *                          a horizontal axis
77              * @param SampleRate      - current sample rate of the engines
78              *                          audio output signal
79              */
trigger(float Frequency,LFO::start_level_t StartLevel,uint16_t InternalDepth,uint16_t ExtControlDepth,bool FlipPhase,unsigned int SampleRate)80             void trigger(float Frequency, LFO::start_level_t StartLevel, uint16_t InternalDepth, uint16_t ExtControlDepth, bool FlipPhase, unsigned int SampleRate) {
81                 this->Frequency = Frequency;
82                 this->ScriptFrequencyFactor = this->ScriptDepthFactor = 1.f; // reset for new voice
83                 this->InternalDepth = (InternalDepth / 1200.0f) * this->Max;
84                 this->ExtControlDepthCoeff = (((float) ExtControlDepth / 1200.0f) / 127.0f) * this->Max;
85                 this->pFinalDepth = NULL;
86                 this->pFinalFrequency = NULL;
87 
88                 const float freq = Frequency * this->ScriptFrequencyFactor;
89                 c = 2.0f * M_PI * freq / (float) SampleRate;
90 
91                 switch (StartLevel) {
92                     case LFO::start_level_mid:
93                         this->startPhase = (FlipPhase) ? 0.5 * M_PI : 1.5 * M_PI; // 90� or 270�
94                         break;
95                     case LFO::start_level_max:
96                         this->startPhase = (FlipPhase) ? M_PI : 0.0; // 180� or 0�
97                         break;
98                     case LFO::start_level_min:
99                         this->startPhase = (FlipPhase) ? 0.0 : M_PI; // 0� or 180�
100                         break;
101                 }
102                 real = cos(this->startPhase);
103                 imag = sin(this->startPhase);
104             }
105 
106             /**
107              * Should be invoked after the LFO is triggered with StartLevel
108              * start_level_min.
109              * @param phase From 0 to 360 degrees.
110              */
setPhase(float phase)111             void setPhase(float phase) {
112                 if (phase < 0) phase = 0;
113                 if (phase > 360) phase = 360;
114                 phase = phase / 360.0f * 2 * M_PI;
115                 real = cos(this->startPhase + phase);
116                 imag = sin(this->startPhase + phase);
117             }
118 
setFrequency(float Frequency,unsigned int SampleRate)119             void setFrequency(float Frequency, unsigned int SampleRate) {
120                 this->Frequency = Frequency;
121                 const float freq = Frequency * this->ScriptFrequencyFactor;
122                 c = 2.0f * M_PI * freq / (float) SampleRate;
123             }
124 
setScriptDepthFactor(float factor,bool isFinal)125             void setScriptDepthFactor(float factor, bool isFinal) {
126                 this->ScriptDepthFactor = factor;
127                 // set or reset this script depth parameter to be the sole
128                 // source for the LFO depth
129                 if (isFinal && !this->pFinalDepth)
130                     this->pFinalDepth = &this->ScriptDepthFactor;
131                 else if (!isFinal && this->pFinalDepth == &this->ScriptDepthFactor)
132                     this->pFinalDepth = NULL;
133                 // recalculate upon new depth
134                 updateByMIDICtrlValue(this->ExtControlValue);
135             }
136 
setScriptFrequencyFactor(float factor,unsigned int SampleRate)137             void setScriptFrequencyFactor(float factor, unsigned int SampleRate) {
138                 this->ScriptFrequencyFactor = factor;
139                 // in case script frequency was set as "final" value before,
140                 // reset it so that all sources are processed from now on
141                 if (this->pFinalFrequency == &this->ScriptFrequencyFactor)
142                     this->pFinalFrequency = NULL;
143                 // recalculate upon new frequency
144                 setFrequency(this->Frequency, SampleRate);
145             }
146 
setScriptFrequencyFinal(float hz,unsigned int SampleRate)147             void setScriptFrequencyFinal(float hz, unsigned int SampleRate) {
148                 this->ScriptFrequencyFactor = hz;
149                 // assign script's given frequency as sole source for the LFO
150                 // frequency, thus ignore all other sources
151                 if (!this->pFinalFrequency)
152                     this->pFinalFrequency = &this->ScriptFrequencyFactor;
153                 // recalculate upon new frequency
154                 setFrequency(this->Frequency, SampleRate);
155             }
156 
157         private:
158             float c;
159             float real;
160             float imag;
161             float normalizer;
162             float offset;
163             double startPhase;
164     };
165 
166 } // namespace LinuxSampler
167 
168 #endif // LS_LFOSINE_NUMERIC_COMPLEX_NR_H
169