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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3561 - (hide 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 schoenebeck 717 /***************************************************************************
2     * *
3 schoenebeck 3561 * Copyright (C) 2005 - 2019 Christian Schoenebeck *
4 schoenebeck 717 * *
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 schoenebeck 1681 #define AMP2 -0.11425509f
28 schoenebeck 717
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 schoenebeck 721 return (real1 + real2 * AMP2) * normalizer + offset;
61 schoenebeck 717 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 schoenebeck 3118 inline void updateByMIDICtrlValue(const uint16_t& ExtControlValue) {
71     this->ExtControlValue = ExtControlValue;
72    
73     const float max = (this->InternalDepth + ExtControlValue * this->ExtControlDepthCoeff) * this->ScriptDepthFactor;
74 schoenebeck 721 if (RANGE == range_unsigned) {
75 schoenebeck 1681 const float harmonicCompensation = 1.0f + fabsf(AMP2); // to compensate the compensation ;) (see trigger())
76 schoenebeck 717 normalizer = max * 0.5f;
77 schoenebeck 721 offset = normalizer * harmonicCompensation;
78     } else { // signed range
79 schoenebeck 717 normalizer = max;
80 schoenebeck 721 }
81 schoenebeck 717 }
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 schoenebeck 721 * @param FlipPhase - inverts the oscillator wave against
93     * a horizontal axis
94 schoenebeck 717 * @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 schoenebeck 3118 this->Frequency = Frequency;
99     this->ScriptFrequencyFactor = this->ScriptDepthFactor = 1.f; // reset for new voice
100 schoenebeck 1681 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 schoenebeck 721 this->ExtControlDepthCoeff = (((float) ExtControlDepth / 1200.0f) / 127.0f) * this->Max / harmonicCompensation;
103 schoenebeck 3561 this->pFinalDepth = NULL;
104     this->pFinalFrequency = NULL;
105 schoenebeck 717
106 schoenebeck 3118 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 schoenebeck 717
110 schoenebeck 721 double phi; // phase displacement
111 schoenebeck 717 switch (StartLevel) {
112 schoenebeck 721 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 schoenebeck 717 case start_level_max:
117 schoenebeck 721 phi = (FlipPhase) ? M_PI : 0.0; // 180� or 0�
118 schoenebeck 717 break;
119     case start_level_min:
120 schoenebeck 721 phi = (FlipPhase) ? 0.0 : M_PI; // 0� or 180�
121 schoenebeck 717 break;
122     }
123     real1 = real2 = cos(phi);
124     imag1 = imag2 = sin(phi);
125     }
126 iliev 2225
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 iliev 2227
143     void setFrequency(float Frequency, unsigned int SampleRate) {
144 schoenebeck 3118 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 iliev 2227 }
149 schoenebeck 717
150 schoenebeck 3561 void setScriptDepthFactor(float factor, bool isFinal) {
151 schoenebeck 3118 this->ScriptDepthFactor = factor;
152 schoenebeck 3561 // 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 schoenebeck 3118 updateByMIDICtrlValue(this->ExtControlValue);
160     }
161    
162     void setScriptFrequencyFactor(float factor, unsigned int SampleRate) {
163     this->ScriptFrequencyFactor = factor;
164 schoenebeck 3561 // 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 schoenebeck 3118 setFrequency(this->Frequency, SampleRate);
170     }
171    
172 schoenebeck 3561 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 schoenebeck 717 private:
183     float c1;
184     float c2;
185     float real1;
186     float imag1;
187     float real2;
188     float imag2;
189     float normalizer;
190 schoenebeck 721 float offset;
191 schoenebeck 717 };
192    
193     } // namespace LinuxSampler
194    
195     #endif // __LS_LFOTRIANGLEDIHARMONIC_H__

  ViewVC Help
Powered by ViewVC