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_LFOSINE_NUMERIC_COMPLEX_NR_H 11 #define LS_LFOSINE_NUMERIC_COMPLEX_NR_H 12 13 #include "LFOBase.h" 14 15 namespace LinuxSampler { 16 17 /** @brief Sine LFO (numeric, complex nr implementation) 18 * 19 * This is a Sinus Low Frequency Oscillator implementation using a complex 20 * number with numeric (descrete) math as basis for its oscillation. 21 */ 22 template<LFO::range_type_t RANGE> 23 class LFOSineNumericComplexNr : public LFOBase<RANGE> { 24 public: 25 26 /** 27 * Constructor 28 * 29 * @param Max - maximum value of the output levels 30 */ LFOSineNumericComplexNr(float Max)31 LFOSineNumericComplexNr(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 real -= c * imag; 42 imag += c * real; 43 if (RANGE == LFO::range_unsigned) 44 return real * normalizer + offset; 45 else /* signed range */ 46 return real * normalizer; 47 } 48 49 /** 50 * Update LFO depth with a new external controller value. 51 * 52 * @param ExtControlValue - new external controller value 53 */ updateByMIDICtrlValue(const uint16_t & ExtControlValue)54 inline void updateByMIDICtrlValue(const uint16_t& ExtControlValue) { 55 this->ExtControlValue = ExtControlValue; 56 57 const float max = (this->InternalDepth + ExtControlValue * this->ExtControlDepthCoeff) * this->ScriptDepthFactor; 58 if (RANGE == LFO::range_unsigned) { 59 normalizer = max * 0.5f; 60 offset = normalizer; 61 } else { // signed range 62 normalizer = 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->ScriptFrequencyFactor = this->ScriptDepthFactor = 1.f; // reset for new voice 83 this->InternalDepth = (InternalDepth / 1200.0f) * this->Max; 84 this->ExtControlDepthCoeff = (((float) ExtControlDepth / 1200.0f) / 127.0f) * this->Max; 85 this->pFinalDepth = NULL; 86 this->pFinalFrequency = NULL; 87 88 const float freq = Frequency * this->ScriptFrequencyFactor; 89 c = 2.0f * M_PI * freq / (float) SampleRate; 90 91 switch (StartLevel) { 92 case LFO::start_level_mid: 93 this->startPhase = (FlipPhase) ? 0.5 * M_PI : 1.5 * M_PI; // 90� or 270� 94 break; 95 case LFO::start_level_max: 96 this->startPhase = (FlipPhase) ? M_PI : 0.0; // 180� or 0� 97 break; 98 case LFO::start_level_min: 99 this->startPhase = (FlipPhase) ? 0.0 : M_PI; // 0� or 180� 100 break; 101 } 102 real = cos(this->startPhase); 103 imag = sin(this->startPhase); 104 } 105 106 /** 107 * Should be invoked after the LFO is triggered with StartLevel 108 * start_level_min. 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 = phase / 360.0f * 2 * M_PI; 115 real = cos(this->startPhase + phase); 116 imag = sin(this->startPhase + 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 c = 2.0f * M_PI * freq / (float) SampleRate; 123 } 124 setScriptDepthFactor(float factor,bool isFinal)125 void setScriptDepthFactor(float factor, bool isFinal) { 126 this->ScriptDepthFactor = factor; 127 // set or reset this script depth parameter to be the sole 128 // source for the LFO depth 129 if (isFinal && !this->pFinalDepth) 130 this->pFinalDepth = &this->ScriptDepthFactor; 131 else if (!isFinal && this->pFinalDepth == &this->ScriptDepthFactor) 132 this->pFinalDepth = NULL; 133 // recalculate upon new depth 134 updateByMIDICtrlValue(this->ExtControlValue); 135 } 136 setScriptFrequencyFactor(float factor,unsigned int SampleRate)137 void setScriptFrequencyFactor(float factor, unsigned int SampleRate) { 138 this->ScriptFrequencyFactor = factor; 139 // in case script frequency was set as "final" value before, 140 // reset it so that all sources are processed from now on 141 if (this->pFinalFrequency == &this->ScriptFrequencyFactor) 142 this->pFinalFrequency = NULL; 143 // recalculate upon new frequency 144 setFrequency(this->Frequency, SampleRate); 145 } 146 setScriptFrequencyFinal(float hz,unsigned int SampleRate)147 void setScriptFrequencyFinal(float hz, unsigned int SampleRate) { 148 this->ScriptFrequencyFactor = hz; 149 // assign script's given frequency as sole source for the LFO 150 // frequency, thus ignore all other sources 151 if (!this->pFinalFrequency) 152 this->pFinalFrequency = &this->ScriptFrequencyFactor; 153 // recalculate upon new frequency 154 setFrequency(this->Frequency, SampleRate); 155 } 156 157 private: 158 float c; 159 float real; 160 float imag; 161 float normalizer; 162 float offset; 163 double startPhase; 164 }; 165 166 } // namespace LinuxSampler 167 168 #endif // LS_LFOSINE_NUMERIC_COMPLEX_NR_H 169