/[svn]/linuxsampler/trunk/src/engines/common/LFOTriangleDiHarmonic.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/common/LFOTriangleDiHarmonic.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3612 - (show annotations) (download) (as text)
Mon Sep 30 18:03:43 2019 UTC (4 years, 5 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 9977 byte(s)
Added new LFO implementations:

* Added int math square LFO implementation.

* Added int math saw LFO implementation.

* Added numeric complex nr sine LFO implementation.

* Added public API C++ class "LFO", which is a cluster class
  encapsulating all the sampler's LFO implementations to be used by
  3rd party applications (e.g. by Gigedit).

* Marked class LFOTriangleDiHarmonic as deprecated
  (will be removed in future).

* Added LFOAll.h which includes all LFO implementation's header files.

* Fixed benchmarks/triang.cpp falsely having favoured "int math abs"
  algorithm (since result of 2nd run was not accumulated).

* Added benchmark for saw wave (benchmarks/saw.cpp).

* Added benchmark for sine wave (benchmarks/sine.cpp).

* Added benchmark for square wave (benchmarks/square.cpp).

* Increased amount of benchmarks runs by factor 6 to achieve benchmark
  times which are large enough on modern systems.

* Cleanup of LFO APIs.

* Bumped version (2.1.1.svn18).

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 */
56 LFOTriangleDiHarmonic(float Max) : LFOBase<RANGE>::LFOBase(Max) {
57 }
58
59 /**
60 * Calculates exactly one sample point of the LFO wave.
61 *
62 * @returns next LFO level
63 */
64 inline float render() {
65 real1 -= c1 * imag1;
66 imag1 += c1 * real1;
67 real2 -= c2 * imag2;
68 imag2 += c2 * real2;
69 if (RANGE == LFO::range_unsigned)
70 return (real1 + real2 * AMP2) * normalizer + offset;
71 else /* signed range */
72 return (real1 + real2 * AMP2) * normalizer;
73 }
74
75 /**
76 * Update LFO depth with a new external controller value.
77 *
78 * @param ExtControlValue - new external controller value
79 */
80 inline void updateByMIDICtrlValue(const uint16_t& ExtControlValue) {
81 this->ExtControlValue = ExtControlValue;
82
83 const float max = (this->InternalDepth + ExtControlValue * this->ExtControlDepthCoeff) * this->ScriptDepthFactor;
84 if (RANGE == LFO::range_unsigned) {
85 const float harmonicCompensation = 1.0f + fabsf(AMP2); // to compensate the compensation ;) (see trigger())
86 normalizer = max * 0.5f;
87 offset = normalizer * harmonicCompensation;
88 } else { // signed range
89 normalizer = max;
90 }
91 }
92
93 /**
94 * Will be called by the voice when the key / voice was triggered.
95 *
96 * @param Frequency - frequency of the oscillator in Hz
97 * @param StartLevel - on which level the wave should start
98 * @param InternalDepth - firm, internal oscillator amplitude
99 * @param ExtControlDepth - defines how strong the external MIDI
100 * controller has influence on the
101 * oscillator amplitude
102 * @param FlipPhase - inverts the oscillator wave against
103 * a horizontal axis
104 * @param SampleRate - current sample rate of the engines
105 * audio output signal
106 */
107 void trigger(float Frequency, LFO::start_level_t StartLevel, uint16_t InternalDepth, uint16_t ExtControlDepth, bool FlipPhase, unsigned int SampleRate) {
108 this->Frequency = Frequency;
109 this->ScriptFrequencyFactor = this->ScriptDepthFactor = 1.f; // reset for new voice
110 const float harmonicCompensation = 1.0f + fabsf(AMP2); // to compensate the 2nd harmonic's amplitude overhead
111 this->InternalDepth = (InternalDepth / 1200.0f) * this->Max / harmonicCompensation;
112 this->ExtControlDepthCoeff = (((float) ExtControlDepth / 1200.0f) / 127.0f) * this->Max / harmonicCompensation;
113 this->pFinalDepth = NULL;
114 this->pFinalFrequency = NULL;
115
116 const float freq = Frequency * this->ScriptFrequencyFactor;
117 c1 = 2.0f * M_PI * freq / (float) SampleRate;
118 c2 = 2.0f * M_PI * freq / (float) SampleRate * 3.0f;
119
120 double phi; // phase displacement
121 switch (StartLevel) {
122 case LFO::start_level_mid:
123 //FIXME: direct jumping to 90� and 270� doesn't work out due to numeric accuracy problems (causes wave deformation)
124 //phi = (FlipPhase) ? 0.5 * M_PI : 1.5 * M_PI; // 90� or 270�
125 //break;
126 case LFO::start_level_max:
127 phi = (FlipPhase) ? M_PI : 0.0; // 180� or 0�
128 break;
129 case LFO::start_level_min:
130 phi = (FlipPhase) ? 0.0 : M_PI; // 0� or 180�
131 break;
132 }
133 real1 = real2 = cos(phi);
134 imag1 = imag2 = sin(phi);
135 }
136
137 /**
138 * Should be invoked after the LFO is triggered with StartLevel
139 * start_level_min.
140 * @param phase From 0 to 360 degrees.
141 */
142 void setPhase(float phase) {
143 if (phase < 0) phase = 0;
144 if (phase > 360) phase = 360;
145 phase /= 360.0f;
146
147 // FIXME: too heavy?
148 float steps = 1.0f / (c1 / (2.0f * M_PI)); // number of steps for one cycle
149 steps *= phase + 0.25f;
150 for (int i = 0; i < steps; i++) render();
151 }
152
153 void setFrequency(float Frequency, unsigned int SampleRate) {
154 this->Frequency = Frequency;
155 const float freq = Frequency * this->ScriptFrequencyFactor;
156 c1 = 2.0f * M_PI * freq / (float) SampleRate;
157 c2 = 2.0f * M_PI * freq / (float) SampleRate * 3.0f;
158 }
159
160 void setScriptDepthFactor(float factor, bool isFinal) {
161 this->ScriptDepthFactor = factor;
162 // set or reset this script depth parameter to be the sole
163 // source for the LFO depth
164 if (isFinal && !this->pFinalDepth)
165 this->pFinalDepth = &this->ScriptDepthFactor;
166 else if (!isFinal && this->pFinalDepth == &this->ScriptDepthFactor)
167 this->pFinalDepth = NULL;
168 // recalculate upon new depth
169 updateByMIDICtrlValue(this->ExtControlValue);
170 }
171
172 void setScriptFrequencyFactor(float factor, unsigned int SampleRate) {
173 this->ScriptFrequencyFactor = factor;
174 // in case script frequency was set as "final" value before,
175 // reset it so that all sources are processed from now on
176 if (this->pFinalFrequency == &this->ScriptFrequencyFactor)
177 this->pFinalFrequency = NULL;
178 // recalculate upon new frequency
179 setFrequency(this->Frequency, SampleRate);
180 }
181
182 void setScriptFrequencyFinal(float hz, unsigned int SampleRate) {
183 this->ScriptFrequencyFactor = hz;
184 // assign script's given frequency as sole source for the LFO
185 // frequency, thus ignore all other sources
186 if (!this->pFinalFrequency)
187 this->pFinalFrequency = &this->ScriptFrequencyFactor;
188 // recalculate upon new frequency
189 setFrequency(this->Frequency, SampleRate);
190 }
191
192 private:
193 float c1;
194 float c2;
195 float real1;
196 float imag1;
197 float real2;
198 float imag2;
199 float normalizer;
200 float offset;
201 };
202
203 } // namespace LinuxSampler
204
205 #endif // __LS_LFOTRIANGLEDIHARMONIC_H__

  ViewVC Help
Powered by ViewVC