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_LFOTRIANGLEDIHARMONIC_H__ 22 #define __LS_LFOTRIANGLEDIHARMONIC_H__ 23 24 #include "LFOBase.h" 25 26 // amplitue of 2nd harmonic (to approximate the triangular wave) 27 #define AMP2 -0.11425509f 28 29 namespace LinuxSampler { 30 31 /** @brief Triangle LFO (di-harmonic implementation) 32 * 33 * This is a triangle Low Frequency Oscillator implementation which uses 34 * a di-harmonic solution. This means it sums up two harmonics 35 * (sinusoids) to approximate a triangular wave. 36 * 37 * @deprecated This class will probably be removed in future. Reason: The 38 * resulting wave form is not similar enough to a triangular wave. to 39 * achieve a more appropriate triangular wave form, this class would need 40 * to use more harmonics, but that in turn would make runtime performance of 41 * this class even worse. And since it currently seems to perform worst 42 * already among all triangular wave implementations on all known 43 * architectures, doing that required harmonics change currently does not 44 * make sense. Furthermore the detailed behaviour of the other triangular 45 * LFO implementations had been fixed in the meantime; this one not. 46 */ 47 template<LFO::range_type_t RANGE> 48 class DEPRECATED_API LFOTriangleDiHarmonic : public LFOBase<RANGE> { 49 public: 50 51 /** 52 * Constructor 53 * 54 * @param Max - maximum value of the output levels 55 */ LFOTriangleDiHarmonic(float Max)56 LFOTriangleDiHarmonic(float Max) : LFOBase<RANGE>::LFOBase(Max) { 57 //NOTE: DO NOT add any custom initialization here, since it would break LFOCluster construction ! 58 } 59 60 /** 61 * Calculates exactly one sample point of the LFO wave. 62 * 63 * @returns next LFO level 64 */ render()65 inline float render() { 66 real1 -= c1 * imag1; 67 imag1 += c1 * real1; 68 real2 -= c2 * imag2; 69 imag2 += c2 * real2; 70 if (RANGE == LFO::range_unsigned) 71 return (real1 + real2 * AMP2) * normalizer + offset; 72 else /* signed range */ 73 return (real1 + real2 * AMP2) * normalizer; 74 } 75 76 /** 77 * Update LFO depth with a new external controller value. 78 * 79 * @param ExtControlValue - new external controller value 80 */ updateByMIDICtrlValue(const uint16_t & ExtControlValue)81 inline void updateByMIDICtrlValue(const uint16_t& ExtControlValue) { 82 this->ExtControlValue = ExtControlValue; 83 84 const float max = (this->InternalDepth + ExtControlValue * this->ExtControlDepthCoeff) * this->ScriptDepthFactor; 85 if (RANGE == LFO::range_unsigned) { 86 const float harmonicCompensation = 1.0f + fabsf(AMP2); // to compensate the compensation ;) (see trigger()) 87 normalizer = max * 0.5f; 88 offset = normalizer * harmonicCompensation; 89 } else { // signed range 90 normalizer = max; 91 } 92 } 93 94 /** 95 * Will be called by the voice when the key / voice was triggered. 96 * 97 * @param Frequency - frequency of the oscillator in Hz 98 * @param StartLevel - on which level the wave should start 99 * @param InternalDepth - firm, internal oscillator amplitude 100 * @param ExtControlDepth - defines how strong the external MIDI 101 * controller has influence on the 102 * oscillator amplitude 103 * @param FlipPhase - inverts the oscillator wave against 104 * a horizontal axis 105 * @param SampleRate - current sample rate of the engines 106 * audio output signal 107 */ trigger(float Frequency,LFO::start_level_t StartLevel,uint16_t InternalDepth,uint16_t ExtControlDepth,bool FlipPhase,unsigned int SampleRate)108 void trigger(float Frequency, LFO::start_level_t StartLevel, uint16_t InternalDepth, uint16_t ExtControlDepth, bool FlipPhase, unsigned int SampleRate) { 109 this->Frequency = Frequency; 110 this->ScriptFrequencyFactor = this->ScriptDepthFactor = 1.f; // reset for new voice 111 const float harmonicCompensation = 1.0f + fabsf(AMP2); // to compensate the 2nd harmonic's amplitude overhead 112 this->InternalDepth = (InternalDepth / 1200.0f) * this->Max / harmonicCompensation; 113 this->ExtControlDepthCoeff = (((float) ExtControlDepth / 1200.0f) / 127.0f) * this->Max / harmonicCompensation; 114 this->pFinalDepth = NULL; 115 this->pFinalFrequency = NULL; 116 117 const float freq = Frequency * this->ScriptFrequencyFactor; 118 c1 = 2.0f * M_PI * freq / (float) SampleRate; 119 c2 = 2.0f * M_PI * freq / (float) SampleRate * 3.0f; 120 121 double phi; // phase displacement 122 switch (StartLevel) { 123 case LFO::start_level_mid: 124 //FIXME: direct jumping to 90� and 270� doesn't work out due to numeric accuracy problems (causes wave deformation) 125 //phi = (FlipPhase) ? 0.5 * M_PI : 1.5 * M_PI; // 90� or 270� 126 //break; 127 case LFO::start_level_max: 128 phi = (FlipPhase) ? M_PI : 0.0; // 180� or 0� 129 break; 130 case LFO::start_level_min: 131 phi = (FlipPhase) ? 0.0 : M_PI; // 0� or 180� 132 break; 133 } 134 real1 = real2 = cos(phi); 135 imag1 = imag2 = sin(phi); 136 } 137 138 /** 139 * Should be invoked after the LFO is triggered with StartLevel 140 * start_level_min. 141 * @param phase From 0 to 360 degrees. 142 */ setPhase(float phase)143 void setPhase(float phase) { 144 if (phase < 0) phase = 0; 145 if (phase > 360) phase = 360; 146 phase /= 360.0f; 147 148 // FIXME: too heavy? 149 float steps = 1.0f / (c1 / (2.0f * M_PI)); // number of steps for one cycle 150 steps *= phase + 0.25f; 151 for (int i = 0; i < steps; i++) render(); 152 } 153 setFrequency(float Frequency,unsigned int SampleRate)154 void setFrequency(float Frequency, unsigned int SampleRate) { 155 this->Frequency = Frequency; 156 const float freq = Frequency * this->ScriptFrequencyFactor; 157 c1 = 2.0f * M_PI * freq / (float) SampleRate; 158 c2 = 2.0f * M_PI * freq / (float) SampleRate * 3.0f; 159 } 160 setScriptDepthFactor(float factor,bool isFinal)161 void setScriptDepthFactor(float factor, bool isFinal) { 162 this->ScriptDepthFactor = factor; 163 // set or reset this script depth parameter to be the sole 164 // source for the LFO depth 165 if (isFinal && !this->pFinalDepth) 166 this->pFinalDepth = &this->ScriptDepthFactor; 167 else if (!isFinal && this->pFinalDepth == &this->ScriptDepthFactor) 168 this->pFinalDepth = NULL; 169 // recalculate upon new depth 170 updateByMIDICtrlValue(this->ExtControlValue); 171 } 172 setScriptFrequencyFactor(float factor,unsigned int SampleRate)173 void setScriptFrequencyFactor(float factor, unsigned int SampleRate) { 174 this->ScriptFrequencyFactor = factor; 175 // in case script frequency was set as "final" value before, 176 // reset it so that all sources are processed from now on 177 if (this->pFinalFrequency == &this->ScriptFrequencyFactor) 178 this->pFinalFrequency = NULL; 179 // recalculate upon new frequency 180 setFrequency(this->Frequency, SampleRate); 181 } 182 setScriptFrequencyFinal(float hz,unsigned int SampleRate)183 void setScriptFrequencyFinal(float hz, unsigned int SampleRate) { 184 this->ScriptFrequencyFactor = hz; 185 // assign script's given frequency as sole source for the LFO 186 // frequency, thus ignore all other sources 187 if (!this->pFinalFrequency) 188 this->pFinalFrequency = &this->ScriptFrequencyFactor; 189 // recalculate upon new frequency 190 setFrequency(this->Frequency, SampleRate); 191 } 192 193 private: 194 float c1; 195 float c2; 196 float real1; 197 float imag1; 198 float real2; 199 float imag2; 200 float normalizer; 201 float offset; 202 }; 203 204 } // namespace LinuxSampler 205 206 #endif // __LS_LFOTRIANGLEDIHARMONIC_H__ 207