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