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