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