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