/[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 3561 - (show annotations) (download) (as text)
Fri Aug 23 11:44:00 2019 UTC (4 years, 8 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 9234 byte(s)
NKSP: Added standard units support for numbers and final "!" operator:

* NKSP strictness: Variable names, function names and preprocessor condition
  names must start with a regular character (a-z or A-Z); starting them with
  a digit or underscore is no longer allowed.

* NKSP parser fix: equal comparison operator "=" and not equal comparison
  operator "#" must only accept integer operands.

* NKSP language: Implemented support for standard units like Hertz, seconds,
  Bel including support for metric unit prefixes; so one can now e.g.
  conveniently use numbers in scripts like "5us" meaning "5 microseconds",
  or e.g. "12kHz" meaning "12 kilo Hertz", or e.g. "-14mdB" meaning
  "minus 14 Millidecibel", or e.g. "28c" meaning "28 cents" (for tuning).

* NKSP language: Introduced "final" operator "!" which is specifically
  intended for synthesis parameter values to denote that the synthesis
  parameter value is intended to be the "final" value for that synthesis
  parameter that should explicitly be used by the engine and thus causing
  the sampler engine to ignore all other modulation sources for the same
  synthesis parameter (like e.g. LFO, EG); by simply prefixing a value,
  variable or formula with this new "!" operator the expression is marked as
  being "final".

* Bumped version (2.1.1.svn4).

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

  ViewVC Help
Powered by ViewVC