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_LFOSAWINTMATHNEW_H
11 #define LS_LFOSAWINTMATHNEW_H
12 
13 #include <stdlib.h>
14 #include "LFOBase.h"
15 
16 namespace LinuxSampler {
17 
18     /** @brief Saw LFO (int math implementation)
19      *
20      * This is a Saw Low Frequency Oscillator which uses pure integer
21      * math (without branches) to synthesize the saw wave.
22      */
23     template<LFO::range_type_t RANGE>
24     class LFOSawIntMathNew : public LFOBase<RANGE> {
25         public:
26             /**
27              * Constructor
28              *
29              * @param Max - maximum value of the output levels
30              */
LFOSawIntMathNew(float Max)31             LFOSawIntMathNew(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                 this->slope += this->c;
42                 if (RANGE == LFO::range_unsigned)
43                     return this->denormalizer * float(this->slope);
44                 else // signed range
45                     return this->denormalizer * float(int(this->slope) + this->offset);
46             }
47 
48             /**
49              * Update LFO depth with a new external controller value.
50              *
51              * @param ExtControlValue - new external controller value
52              */
updateByMIDICtrlValue(const uint16_t & ExtControlValue)53             inline void updateByMIDICtrlValue(const uint16_t& ExtControlValue) {
54                 this->ExtControlValue = ExtControlValue;
55 
56                 const unsigned int intLimit = (unsigned int) -1; // all 0xFFFF...
57                 const float max = (this->InternalDepth + ExtControlValue * this->ExtControlDepthCoeff) * this->ScriptDepthFactor;
58                 if (RANGE == LFO::range_unsigned) {
59                     denormalizer = max / (float) intLimit / 2.0;
60                 } else { // signed range
61                     denormalizer = max / (float) intLimit * 2.0;
62                     offset       = -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->InternalDepth        = (InternalDepth / 1200.0f) * this->Max;
83                 this->ExtControlDepthCoeff = (((float) ExtControlDepth / 1200.0f) / 127.0f) * this->Max;
84                 this->ScriptFrequencyFactor = this->ScriptDepthFactor = 1.f; // reset for new voice
85                 if (RANGE == LFO::range_unsigned) {
86                     this->InternalDepth        *= 2.0f;
87                     this->ExtControlDepthCoeff *= 2.0f;
88                 }
89                 this->pFinalDepth = NULL;
90                 this->pFinalFrequency = NULL;
91 
92                 this->flipPhaseFactor = FlipPhase ? -1 : 1;
93                 const unsigned int intLimit = (unsigned int) -1; // all 0xFFFF...
94                 const float freq = Frequency * this->ScriptFrequencyFactor;
95                 const float r = freq / (float) SampleRate; // frequency alteration quotient
96                 c = int(intLimit * r * this->flipPhaseFactor);
97 
98                 const int slopeAtMid = (RANGE == LFO::range_unsigned) ? intLimit / 2 : intLimit;
99                 const int slopeAtMin = (RANGE == LFO::range_unsigned) ? intLimit : intLimit / 2;
100 
101                 switch (StartLevel) {
102                     case LFO::start_level_mid:
103                         //slope = (FlipPhase) ? slopeAtMin : slopeAtMid;
104                         slope = slopeAtMid;
105                         break;
106 
107                     // with saw function, min and max are actually always the same thing
108                     // (and no matter if FlipPhase or not)
109                     case LFO::start_level_max:
110                     case LFO::start_level_min:
111                         slope = slopeAtMin;
112                         break;
113                 }
114             }
115 
116             /**
117              * Should be invoked after the LFO is triggered.
118              * @param phase From 0 to 360 degrees.
119              */
setPhase(float phase)120             void setPhase(float phase) {
121                 if (phase < 0) phase = 0;
122                 if (phase > 360) phase = 360;
123                 phase /= 360.0f;
124                 const unsigned int intLimit = (unsigned int) -1; // all 0xFFFF...
125                 slope += intLimit * phase * flipPhaseFactor;
126             }
127 
setFrequency(float Frequency,unsigned int SampleRate)128             void setFrequency(float Frequency, unsigned int SampleRate) {
129                 this->Frequency = Frequency;
130                 const float freq = Frequency * this->ScriptFrequencyFactor;
131                 const unsigned int intLimit = (unsigned int) -1; // all 0xFFFF...
132                 float r = freq / (float) SampleRate; // frequency alteration quotient
133                 c = int(intLimit * r * this->flipPhaseFactor);
134             }
135 
setScriptDepthFactor(float factor,bool isFinal)136             void setScriptDepthFactor(float factor, bool isFinal) {
137                 this->ScriptDepthFactor = factor;
138                 // set or reset this script depth parameter to be the sole
139                 // source for the LFO depth
140                 if (isFinal && !this->pFinalDepth)
141                     this->pFinalDepth = &this->ScriptDepthFactor;
142                 else if (!isFinal && this->pFinalDepth == &this->ScriptDepthFactor)
143                     this->pFinalDepth = NULL;
144                 // recalculate upon new depth
145                 updateByMIDICtrlValue(this->ExtControlValue);
146             }
147 
setScriptFrequencyFactor(float factor,unsigned int SampleRate)148             void setScriptFrequencyFactor(float factor, unsigned int SampleRate) {
149                 this->ScriptFrequencyFactor = factor;
150                 // in case script frequency was set as "final" value before,
151                 // reset it so that all sources are processed from now on
152                 if (this->pFinalFrequency == &this->ScriptFrequencyFactor)
153                     this->pFinalFrequency = NULL;
154                 // recalculate upon new frequency
155                 setFrequency(this->Frequency, SampleRate);
156             }
157 
setScriptFrequencyFinal(float hz,unsigned int SampleRate)158             void setScriptFrequencyFinal(float hz, unsigned int SampleRate) {
159                 this->ScriptFrequencyFactor = hz;
160                 // assign script's given frequency as sole source for the LFO
161                 // frequency, thus ignore all other sources
162                 if (!this->pFinalFrequency)
163                     this->pFinalFrequency = &this->ScriptFrequencyFactor;
164                 // recalculate upon new frequency
165                 setFrequency(this->Frequency, SampleRate);
166             }
167 
168         protected:
169             unsigned int slope;
170             unsigned int c;
171             float offset; ///< only needed for signed range
172             float denormalizer;
173             float flipPhaseFactor; ///< Factor instead of boolean to avoid branches.
174     };
175 
176 } // namespace LinuxSampler
177 
178 #endif // LS_LFOSAWINTMATHNEW_H
179