tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_ToneGenerator.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14//==============================================================================
15juce::StringArray ToneGeneratorPlugin::getOscTypeNames()
16{
17 return { "Sin", "Triangle", "Saw Up", "Saw Down", "Square", "Noise" };
18}
19
20//==============================================================================
21ToneGeneratorPlugin::ToneGeneratorPlugin (PluginCreationInfo info)
22 : Plugin (info)
23{
24 initialiseOscilators();
25
26 auto um = getUndoManager();
27
28 oscType.referTo (state, IDs::oscType, um, static_cast<float> (OscType::sin));
29 bandLimit.referTo (state, IDs::bandLimit, um, 0.0f);
30 frequency.referTo (state, IDs::frequency, um, 220.0f);
31 level.referTo (state, IDs::level, um, 1.0f);
32
33 oscTypeParam = createDiscreteParameter (*this, "oscType", TRANS("OSC Type"), { 0.0f, (float) getOscTypeNames().size() - 1 }, oscType, getOscTypeNames());
34 bandLimitParam = createDiscreteParameter (*this, "bandLimit", TRANS("Band Limit"), { 0.0f, 1.0f }, bandLimit, { NEEDS_TRANS("Aliased"), NEEDS_TRANS("Band Limited") });
35 frequencyParam = createSuffixedParameter (*this, "frequency", TRANS("Frequency"), { 1.0f, 22050.0f }, 1000.0f, frequency, {});
36 levelParam = createSuffixedParameter (*this, "level", TRANS("Level"), { 0.00001f, 1.0f }, 0.5f, level, {});
37
38 addAutomatableParameter (oscTypeParam);
39 addAutomatableParameter (bandLimitParam);
40 addAutomatableParameter (frequencyParam);
41 addAutomatableParameter (levelParam);
42}
43
44ToneGeneratorPlugin::~ToneGeneratorPlugin()
45{
46 notifyListenersOfDeletion();
47}
48
49const char* ToneGeneratorPlugin::xmlTypeName = "toneGenerator";
50
51void ToneGeneratorPlugin::initialise (const PluginInitialisationInfo& info)
52{
53 scratch.setSize (1, info.blockSizeSamples);
54 auto samplesPerBlock = static_cast<uint32_t> (info.blockSizeSamples);
55
56 sine.prepare ({ sampleRate, static_cast<uint32_t> (samplesPerBlock), 1 });
57 triangle.prepare ({ sampleRate, static_cast<uint32_t> (samplesPerBlock), 1 });
58 sawUp.prepare ({ sampleRate, static_cast<uint32_t> (samplesPerBlock), 1 });
59 sawDown.prepare ({ sampleRate, static_cast<uint32_t> (samplesPerBlock), 1 });
60 square.prepare ({ sampleRate, static_cast<uint32_t> (samplesPerBlock), 1 });
61 noise.prepare ({ sampleRate, static_cast<uint32_t> (samplesPerBlock), 1 });
62}
63
64void ToneGeneratorPlugin::deinitialise()
65{
66}
67
68void ToneGeneratorPlugin::applyToBuffer (const PluginRenderContext& fc)
69{
70 if (fc.destBuffer == nullptr)
71 return;
72
73 SCOPED_REALTIME_CHECK
74 float gain = levelParam->getCurrentValue();
75
76 if (juce::isWithin (gain, 0.0f, 0.00001f))
77 {
78 fc.destBuffer->clear();
79 return;
80 }
81
82 int numSamples = fc.bufferNumSamples;
83 scratch.setSize (1, numSamples, false, false, true);
84
85 bandLimitOsc = getBoolParamValue (*bandLimitParam);
86 float freq = frequencyParam->getCurrentValue();
87 sine.setFrequency (freq);
88 triangle.setFrequency (freq);
89 sawUp.setFrequency (freq);
90 sawDown.setFrequency (freq);
91 square.setFrequency (freq);
92
93 scratch.clear();
94 juce::dsp::AudioBlock<float> block (scratch);
96
97 switch (getTypedParamValue<OscType> (*oscTypeParam))
98 {
99 case OscType::sin: sine.process (context); break;
100 case OscType::triangle: triangle.process (context); break;
101 case OscType::sawUp: sawUp.process (context); break;
102 case OscType::sawDown: sawDown.process (context); break;
103 case OscType::square: square.process (context); break;
104 case OscType::noise: noise.process (context); break;
105 };
106
107 scratch.applyGain (gain);
108
109 for (int i = fc.destBuffer->getNumChannels(); --i >= 0;)
110 fc.destBuffer->copyFrom (i, fc.bufferStartSample, scratch, 0, 0, numSamples);
111}
112
113void ToneGeneratorPlugin::restorePluginStateFromValueTree (const juce::ValueTree& v)
114{
115 copyPropertiesToCachedValues (v, oscType, bandLimit, frequency, level);
116
117 for (auto p : getAutomatableParameters())
118 p->updateFromAttachedValue();
119}
120
121//==============================================================================
122namespace ToneGenHelpers
123{
124 inline int oddEven (int x)
125 {
126 return (x % 2 == 0) ? 1 : -1;
127 }
128}
129
130void ToneGeneratorPlugin::initialiseOscilators()
131{
132 auto sineFunc = [&] (float in) -> float
133 {
134 return std::sin (in);
135 };
136
137 auto triangleFunc = [&] (float in) -> float
138 {
139 if (bandLimitOsc.load())
140 {
141 const double f = frequencyParam->getCurrentValue();
142 const double sr = sampleRate;
143
144 double sum = 0;
145 int k = 1;
146
147 while (f * k < sr / 2)
148 {
149 sum += std::pow (-1, (k - 1) / 2.0f) / (k * k) * sin (k * in);
150 k += 2;
151 }
152
154 }
155
156 return (in < 0 ? in / -juce::MathConstants<float>::pi : in / juce::MathConstants<float>::pi) * 2 - 1;
157 };
158
159 auto sawUpFunc = [&] (float in) -> float
160 {
161 if (bandLimitOsc.load())
162 {
163 const double f = frequencyParam->getCurrentValue();
164 const double sr = sampleRate;
165
166 double sum = 0;
167 int k = 1;
168
169 while (f * k < sr / 2)
170 {
171 sum += ToneGenHelpers::oddEven (k) * std::sin (k * in) / k;
172 k++;
173 }
174
175 return float (-2.0f / juce::MathConstants<float>::pi * sum);
176 }
177
179 };
180
181 auto sawDownFunc = [&] (float in) -> float
182 {
183 if (bandLimitOsc.load())
184 {
185 const double f = frequencyParam->getCurrentValue();
186 const double sr = sampleRate;
187
188 double sum = 0;
189 int k = 1;
190
191 while (f * k < sr / 2)
192 {
193 sum += ToneGenHelpers::oddEven (k) * std::sin (k * in) / k;
194 k++;
195 }
196
197 return float (2.0f / juce::MathConstants<float>::pi * sum);
198 }
199
200 return -(((in + juce::MathConstants<float>::pi) / (2 * juce::MathConstants<float>::pi)) * 2 - 1);
201 };
202
203 auto squareFunc = [&] (float in) -> float
204 {
205 if (bandLimitOsc.load())
206 {
207 const double f = frequencyParam->getCurrentValue();
208 const double sr = sampleRate;
209
210 double sum = 0;
211 int i = 1;
212
213 while (f * (2 * i - 1) < sr / 2)
214 {
215 sum += std::sin ((2 * i - 1) * in) / ((2 * i - 1));
216 i++;
217 }
218
219 return float (4.0f / juce::MathConstants<float>::pi * sum);
220 }
221
222 return in < 0 ? -1.0f : 1.0f;
223 };
224
225 auto noiseFunc = [&] (float) -> float
226 {
227 const float mean = 0.0f;
228 const float stddev = 0.1f;
229
230 static std::default_random_engine generator;
231 static std::normal_distribution<float> dist (mean, stddev);
232
233 return dist (generator);
234 };
235
236 sine.initialise (sineFunc);
237 triangle.initialise (triangleFunc);
238 sawUp.initialise (sawUpFunc);
239 sawDown.initialise (sawDownFunc);
240 square.initialise (squareFunc);
241 noise.initialise (noiseFunc);
242}
243
244}} // namespace tracktion { inline namespace engine
int getNumChannels() const noexcept
void clear() noexcept
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
#define TRANS(stringLiteral)
#define NEEDS_TRANS(stringLiteral)
typedef float
bool isWithin(Type a, Type b, Type tolerance) noexcept
constexpr NumericType square(NumericType n) noexcept
bool getBoolParamValue(const AutomatableParameter &ap)
Returns a bool version of an AutomatableParameter.
Passed into Plugins when they are being initialised, to give them useful contextual information that ...
T pow(T... args)
T sin(T... args)
typedef uint32_t
The context passed to plugin render methods to provide it with buffers to fill.
int bufferNumSamples
The number of samples to write into the audio buffer.
juce::AudioBuffer< float > * destBuffer
The target audio buffer which needs to be filled.
int bufferStartSample
The index of the start point in the audio buffer from which data must be written.