1 /***************************************************************************
2  *                                                                         *
3  *   Copyright (C) 2005 - 2019 Christian Schoenebeck                       *
4  *                                                                         *
5  *   This library is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This library is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this library; if not, write to the Free Software           *
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
18  *   MA  02111-1307  USA                                                   *
19  ***************************************************************************/
20 
21 #ifndef __LS_LFOTRIANGLEINTMATH_H__
22 #define __LS_LFOTRIANGLEINTMATH_H__
23 
24 #include "LFOBase.h"
25 
26 namespace LinuxSampler {
27 
28     /** @brief Triangle LFO (int math implementation)
29      *
30      * This is a triangle Low Frequency Oscillator which uses pure integer
31      * math (without branches) to synthesize the triangular wave.
32      */
33     template<LFO::range_type_t RANGE>
34     class LFOTriangleIntMath : public LFOBase<RANGE> {
35         public:
36 
37             /**
38              * Constructor
39              *
40              * @param Max - maximum value of the output levels
41              */
LFOTriangleIntMath(float Max)42             LFOTriangleIntMath(float Max) : LFOBase<RANGE>::LFOBase(Max) {
43                 //NOTE: DO NOT add any custom initialization here, since it would break LFOCluster construction !
44             }
45 
46             /**
47              * Calculates exactly one sample point of the LFO wave.
48              *
49              * @returns next LFO level
50              */
render()51             inline float render() {
52                 const int signshifts = (sizeof(int) * 8) - 1;
53                 iLevel += c;
54                 const int iSign  = (iLevel >> signshifts) | 1;
55                 if (RANGE == LFO::range_unsigned)
56                     return normalizer * (float) (iSign * iLevel);
57                 else /* signed range */
58                     return normalizer * (float) (iSign * iLevel) + offset;
59             }
60 
61             /**
62              * Update LFO depth with a new external controller value.
63              *
64              * @param ExtControlValue - new external controller value
65              */
updateByMIDICtrlValue(const uint16_t & ExtControlValue)66             inline void updateByMIDICtrlValue(const uint16_t& ExtControlValue) {
67                 this->ExtControlValue = ExtControlValue;
68 
69                 const unsigned int intLimit = (unsigned int) -1; // all 0xFFFF...
70                 const float max = (this->InternalDepth + ExtControlValue * this->ExtControlDepthCoeff) * this->ScriptDepthFactor;
71                 if (RANGE == LFO::range_unsigned) {
72                     normalizer = max / (float) intLimit;
73                 } else { // signed range
74                     normalizer = max / (float) intLimit * 4.0f;
75                     offset     = -max;
76                 }
77             }
78 
79             /**
80              * Will be called by the voice when the key / voice was triggered.
81              *
82              * @param Frequency       - frequency of the oscillator in Hz
83              * @param StartLevel      - on which level the wave should start
84              * @param InternalDepth   - firm, internal oscillator amplitude
85              * @param ExtControlDepth - defines how strong the external MIDI
86              *                          controller has influence on the
87              *                          oscillator amplitude
88              * @param FlipPhase       - inverts the oscillator wave against
89              *                          a horizontal axis
90              * @param SampleRate      - current sample rate of the engines
91              *                          audio output signal
92              */
trigger(float Frequency,LFO::start_level_t StartLevel,uint16_t InternalDepth,uint16_t ExtControlDepth,bool FlipPhase,unsigned int SampleRate)93             void trigger(float Frequency, LFO::start_level_t StartLevel, uint16_t InternalDepth, uint16_t ExtControlDepth, bool FlipPhase, unsigned int SampleRate) {
94                 this->Frequency            = Frequency;
95                 this->InternalDepth        = (InternalDepth / 1200.0f) * this->Max;
96                 this->ExtControlDepthCoeff = (((float) ExtControlDepth / 1200.0f) / 127.0f) * this->Max;
97                 this->ScriptFrequencyFactor = this->ScriptDepthFactor = 1.f; // reset for new voice
98                 if (RANGE == LFO::range_unsigned) {
99                     this->InternalDepth        *= 2.0f;
100                     this->ExtControlDepthCoeff *= 2.0f;
101                 }
102                 this->pFinalDepth = NULL;
103                 this->pFinalFrequency = NULL;
104 
105                 const unsigned int intLimit = (unsigned int) -1; // all 0xFFFF...
106                 const float freq = Frequency * this->ScriptFrequencyFactor;
107                 const float r = freq / (float) SampleRate; // frequency alteration quotient
108                 c = (int) (intLimit * r);
109 
110                 switch (StartLevel) {
111                     case LFO::start_level_max:
112                         iLevel = (FlipPhase) ? 0 : intLimit >> 1;
113                         break;
114                     case LFO::start_level_mid:
115                         iLevel = (FlipPhase) ? intLimit / 4 * 3 : intLimit >> 2;
116                         break;
117                     case LFO::start_level_min:
118                         iLevel = (FlipPhase) ? intLimit >> 1 : 0;
119                         break;
120                 }
121             }
122 
123             /**
124              * Should be invoked after the LFO is triggered.
125              * @param phase From 0 to 360 degrees.
126              */
setPhase(float phase)127             void setPhase(float phase) {
128                 if (phase < 0) phase = 0;
129                 if (phase > 360) phase = 360;
130                 phase /= 360.0f;
131                 const unsigned int intLimit = (unsigned int) -1; // all 0xFFFF...
132                 unsigned int uiPhase = intLimit * phase + iLevel;
133                 if (uiPhase > intLimit / 2) iLevel = uiPhase - intLimit;
134                 else iLevel = uiPhase;
135             }
136 
setFrequency(float Frequency,unsigned int SampleRate)137             void setFrequency(float Frequency, unsigned int SampleRate) {
138                 this->Frequency = Frequency;
139                 const float freq = Frequency * this->ScriptFrequencyFactor;
140                 const unsigned int intLimit = (unsigned int) -1; // all 0xFFFF...
141                 float r = freq / (float) SampleRate; // frequency alteration quotient
142                 c = (int) (intLimit * r);
143             }
144 
setScriptDepthFactor(float factor,bool isFinal)145             void setScriptDepthFactor(float factor, bool isFinal) {
146                 this->ScriptDepthFactor = factor;
147                 // set or reset this script depth parameter to be the sole
148                 // source for the LFO depth
149                 if (isFinal && !this->pFinalDepth)
150                     this->pFinalDepth = &this->ScriptDepthFactor;
151                 else if (!isFinal && this->pFinalDepth == &this->ScriptDepthFactor)
152                     this->pFinalDepth = NULL;
153                 // recalculate upon new depth
154                 updateByMIDICtrlValue(this->ExtControlValue);
155             }
156 
setScriptFrequencyFactor(float factor,unsigned int SampleRate)157             void setScriptFrequencyFactor(float factor, unsigned int SampleRate) {
158                 this->ScriptFrequencyFactor = factor;
159                 // in case script frequency was set as "final" value before,
160                 // reset it so that all sources are processed from now on
161                 if (this->pFinalFrequency == &this->ScriptFrequencyFactor)
162                     this->pFinalFrequency = NULL;
163                 // recalculate upon new frequency
164                 setFrequency(this->Frequency, SampleRate);
165             }
166 
setScriptFrequencyFinal(float hz,unsigned int SampleRate)167             void setScriptFrequencyFinal(float hz, unsigned int SampleRate) {
168                 this->ScriptFrequencyFactor = hz;
169                 // assign script's given frequency as sole source for the LFO
170                 // frequency, thus ignore all other sources
171                 if (!this->pFinalFrequency)
172                     this->pFinalFrequency = &this->ScriptFrequencyFactor;
173                 // recalculate upon new frequency
174                 setFrequency(this->Frequency, SampleRate);
175             }
176 
177         protected:
178             int   iLevel;
179             int   c;
180             float offset; ///< only needed for signed range
181             float normalizer;
182     };
183 
184 } // namespace LinuxSampler
185 
186 #endif // __LS_LFOTRIANGLEINTMATH_H__
187