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