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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_Oscillators.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2018
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7*/
8
9
10namespace tracktion { inline namespace engine
11{
12
13//==============================================================================
14inline int oddEven (int x)
15{
16 return (x % 2 == 0) ? 1 : -1;
17}
18
19//==============================================================================
20static float sine (float phase)
21{
22 return std::sin (phase * 2 * juce::MathConstants<float>::pi);
23}
24
25static float triangle (float phase, float freq, double sampleRate)
26{
27 float sum = 0;
28 int k = 1;
29 while (freq * k < sampleRate / 2)
30 {
31 sum += std::pow (-1.0f, (k - 1.0f) / 2.0f) / (k * k) * std::sin (k * (phase * 2 * juce::MathConstants<float>::pi));
32 k += 2;
33 }
35}
36
37static float sawUp (float phase, float freq, double sampleRate)
38{
39 float sum = 0;
40 int k = 1;
41 while (freq * k < sampleRate / 2)
42 {
43 sum += oddEven (k) * std::sin (k * (phase * 2 * juce::MathConstants<float>::pi)) / k;
44 k++;
45 }
46 return float (-2.0f / juce::MathConstants<float>::pi * sum);
47}
48
49static float sawDown (float phase, float freq, double sampleRate)
50{
51 float sum = 0;
52 int k = 1;
53 while (freq * k < sampleRate / 2)
54 {
55 sum += oddEven (k) * std::sin (k * (phase * 2 * juce::MathConstants<float>::pi)) / k;
56 k++;
57 }
58 return float (2.0f / juce::MathConstants<float>::pi * sum);
59}
60
61//==============================================================================
62void Oscillator::start()
63{
64 static juce::Random r;
65 phase = r.nextFloat();
66}
67
68void Oscillator::setSampleRate (double sr)
69{
70 sampleRate = sr;
71
72 if (lookupTables == nullptr || sampleRate != lookupTables->sampleRate)
73 lookupTables = BandlimitedWaveLookupTables::getLookupTables (sampleRate);
74}
75
76void Oscillator::process (juce::AudioBuffer<float>& buffer, int startSample, int numSamples)
77{
78 if (lookupTables != nullptr)
79 {
80 switch (wave)
81 {
82 case none: break;
83 case sine: processSine (buffer, startSample, numSamples); break;
84 case square: processSquare (buffer, startSample, numSamples); break;
85 case saw: processLookup (buffer, startSample, numSamples, lookupTables->sawUpFunctions); break;
86 case triangle: processLookup (buffer, startSample, numSamples, lookupTables->triangleFunctions); break;
87 case noise: processNoise (buffer, startSample, numSamples); break;
88 }
89 }
90}
91
92void Oscillator::processSine (juce::AudioBuffer<float>& buffer, int startSample, int numSamples)
93{
94 const float frequency = std::min (float (sampleRate) / 2.0f, 440.0f * std::pow (2.0f, (note - 69.0f) / 12.0f));
95 const float period = 1.0f / float (frequency);
96 const float periodInSamples = float (period * sampleRate);
97 const float delta = 1.0f / periodInSamples;
98
99 auto* channels = buffer.getArrayOfWritePointers();
100 const int numChannels = buffer.getNumChannels();
101
102 for (int samp = 0; samp < numSamples; samp++)
103 {
104 float value = lookupTables->sineFunction[phase] * gain;
105 for (int ch = 0; ch < numChannels; ch++)
106 channels[ch][startSample + samp] += value;
107
108 phase += delta;
109 while (phase >= 1.0f)
110 phase -= 1.0f;
111 }
112}
113
114void Oscillator::processNoise (juce::AudioBuffer<float>& buffer, int startSample, int numSamples)
115{
116 auto* channels = buffer.getArrayOfWritePointers();
117 const int numChannels = buffer.getNumChannels();
118
119 for (int samp = 0; samp < numSamples; samp++)
120 {
121 float value = normalDistribution (generator) * gain;
122 for (int ch = 0; ch < numChannels; ch++)
123 channels[ch][startSample + samp] += value;
124 }
125}
126
127void Oscillator::processLookup (juce::AudioBuffer<float>& buffer, int startSample, int numSamples,
129{
130 const float frequency = std::min (float (sampleRate) / 2.0f, 440.0f * std::pow (2.0f, (note - 69.0f) / 12.0f));
131 const float period = 1.0f / float (frequency);
132 const float periodInSamples = float (period * sampleRate);
133 const float delta = 1.0f / periodInSamples;
134
135 auto* channels = buffer.getArrayOfWritePointers();
136 const int numChannels = buffer.getNumChannels();
137
138 int tableIndex = juce::jlimit (0, tableSet.size() - 1, int ((note - 0.5) / lookupTables->tablePerNumNotes));
139
140 auto table = tableSet[tableIndex];
141 jassert (table != nullptr);
142
143 if (table != nullptr)
144 {
145 for (int samp = 0; samp < numSamples; samp++)
146 {
147 float value = table->processSampleUnchecked (phase) * gain;
148 for (int ch = 0; ch < numChannels; ch++)
149 channels[ch][startSample + samp] += value;
150
151 phase += delta;
152 while (phase >= 1.0f)
153 phase -= 1.0f;
154 }
155 }
156}
157
158void Oscillator::processSquare (juce::AudioBuffer<float>& buffer, int startSample, int numSamples)
159{
160 const float frequency = std::min (float (sampleRate) / 2.0f, 440.0f * std::pow (2.0f, (note - 69.0f) / 12.0f));
161 const float period = 1.0f / float (frequency);
162 const float periodInSamples = float (period * sampleRate);
163 const float delta = 1.0f / periodInSamples;
164
165 auto* channels = buffer.getArrayOfWritePointers();
166 const int numChannels = buffer.getNumChannels();
167
168 int tableIndex = juce::jlimit (0, lookupTables->sawUpFunctions.size() - 1, int ((note - 0.5) / lookupTables->tablePerNumNotes));
169
170 auto saw1 = lookupTables->sawUpFunctions[tableIndex];
171 auto saw2 = lookupTables->sawDownFunctions[tableIndex];
172
173 jassert (saw1 != nullptr && saw2 != nullptr);
174
175 if (saw1 != nullptr && saw2 != nullptr)
176 {
177 for (int samp = 0; samp < numSamples; samp++)
178 {
179 float phaseUp = phase + 0.5f * pulseWidth;
180 float phaseDown = phase - 0.5f * pulseWidth;
181
182 if (phaseUp > 1.0f) phaseUp -= 1.0f;
183 if (phaseDown < 0.0f) phaseDown += 1.0f;
184
185 float value = (saw1->processSampleUnchecked (phaseUp) +
186 saw2->processSampleUnchecked (phaseDown)) * gain;
187
188 for (int ch = 0; ch < numChannels; ch++)
189 channels[ch][startSample + samp] += value;
190
191 phase += delta;
192 while (phase >= 1.0f)
193 phase -= 1.0f;
194 }
195 }
196}
197
198//==============================================================================
199MultiVoiceOscillator::MultiVoiceOscillator (int maxVoices)
200{
201 for (int i = 0; i < maxVoices * 2; i++)
202 oscillators.add (new Oscillator());
203}
204
205void MultiVoiceOscillator::start()
206{
207 static juce::Random r;
208
209 for (int i = 0; i < oscillators.size(); i += 2)
210 {
211 float phase = r.nextFloat();
212 oscillators[i + 0]->start (phase);
213 oscillators[i + 1]->start (phase);
214 }
215}
216
217void MultiVoiceOscillator::setSampleRate (double sr)
218{
219 for (auto o : oscillators)
220 o->setSampleRate (sr);
221}
222
223void MultiVoiceOscillator::setWave (Oscillator::Waves w)
224{
225 for (auto o : oscillators)
226 o->setWave (w);
227}
228
229void MultiVoiceOscillator::setNote (float n)
230{
231 note = n;
232}
233
234void MultiVoiceOscillator::setGain (float g)
235{
236 gain = g;
237}
238
239void MultiVoiceOscillator::setPan (float p)
240{
241 pan = p;
242}
243
244void MultiVoiceOscillator::setPulseWidth (float p)
245{
246 for (auto o : oscillators)
247 o->setPulseWidth (p);
248}
249
250void MultiVoiceOscillator::setNumVoices (int n)
251{
252 voices = n;
253}
254
255void MultiVoiceOscillator::setDetune (float d)
256{
257 detune = d;
258}
259
260void MultiVoiceOscillator::setSpread (float s)
261{
262 spread = s;
263}
264
265void MultiVoiceOscillator::process (juce::AudioBuffer<float>& buffer, int startSample, int numSamples)
266{
267 if (voices == 1)
268 {
269 float leftGain = 1.0f - pan;
270 float rightGain = 1.0f + pan;
271
272 for (int i = 0; i < 2; i++)
273 {
274 bool left = (i % 2) == 0;
275 float panGain = left ? leftGain : rightGain;
276
277 float* data = buffer.getWritePointer (left ? 0 : 1, startSample);
278 float* dataPointers[] = {data};
279
280 juce::AudioBuffer<float> channelBuffer (dataPointers, 1, numSamples);
281
282 auto& o = *oscillators[i];
283
284 o.setGain (gain * panGain / voices);
285 o.setNote (note);
286 o.process (channelBuffer, 0, numSamples);
287 }
288 }
289 else
290 {
291 for (int i = 0; i < std::min (voices * 2, oscillators.size()); i++)
292 {
293 int voiceIndex = (i / 2);
294 float localPan = juce::jlimit (-1.0f, 1.0f, ((voiceIndex % 2 == 0) ? 1 : -1) * spread);
295
296 float leftGain = 1.0f - localPan;
297 float rightGain = 1.0f + localPan;
298
299 float base = note - detune / 2;
300 float delta = detune / (voices - 1);
301 bool left = (i % 2) == 0;
302 float panGain = left ? leftGain : rightGain;
303
304 float* data = buffer.getWritePointer (left ? 0 : 1, startSample);
305 float* dataPointers[] = {data};
306
307 juce::AudioBuffer<float> channelBuffer (dataPointers, 1, numSamples);
308
309 auto& o = *oscillators[i];
310
311 o.setGain (gain * panGain / voices);
312 o.setNote (base + delta * (i / 2));
313 o.process (channelBuffer, 0, numSamples);
314 }
315 }
316}
317
318//==============================================================================
320
321BandlimitedWaveLookupTables::Ptr BandlimitedWaveLookupTables::getLookupTables (double sampleRate)
322{
323 for (auto table : tableCache)
324 if (table->sampleRate == sampleRate)
325 return table;
326
327 Ptr table = new BandlimitedWaveLookupTables (sampleRate, 1024);
328 return table;
329}
330
331BandlimitedWaveLookupTables::BandlimitedWaveLookupTables (double sr, int tableSize)
332 : sampleRate (sr),
333 sineFunction ([] (float in) { return sine (in); }, 0.0f, 1.0f, (size_t) tableSize)
334{
335 auto getMidiNoteInHertz = [](float noteNumber)
336 {
337 return 440.0f * std::pow (2.0f, (noteNumber - 69) / 12.0f);
338 };
339
340 auto start = juce::Time::getCurrentTime();
341
342 for (float note = tablePerNumNotes + 0.5f; note < 127; note += tablePerNumNotes)
343 {
344 const float freq = getMidiNoteInHertz (note);
345
346 triangleFunctions.add (new juce::dsp::LookupTableTransform<float> ([freq, sr] (float value)
347 { return triangle (value, freq, sr); }, 0.0f, 1.0f, (size_t) tableSize));
348
349 sawUpFunctions.add (new juce::dsp::LookupTableTransform<float> ([freq, sr] (float value)
350 { return sawUp (value, freq, sr); }, 0.0f, 1.0f, (size_t) tableSize));
351
352 sawDownFunctions.add (new juce::dsp::LookupTableTransform<float> ([freq, sr] (float value)
353 { return sawDown (value, freq, sr); }, 0.0f, 1.0f, (size_t) tableSize));
354 }
355
356 auto elapsed = (juce::Time::getCurrentTime() - start);
357 DBG ("Generating waves: " + juce::String (elapsed.inMilliseconds()) + "ms");
358
359 tableCache.add (this);
360}
361
362BandlimitedWaveLookupTables::~BandlimitedWaveLookupTables()
363{
364 tableCache.removeFirstMatchingValue (this);
365}
366
367}} // namespace tracktion { inline namespace engine
Type * getWritePointer(int channelNumber) noexcept
int getNumChannels() const noexcept
Type *const * getArrayOfWritePointers() noexcept
float nextFloat() noexcept
static Time JUCE_CALLTYPE getCurrentTime() noexcept
T data(T... args)
#define jassert(expression)
#define DBG(textToWrite)
T left(T... args)
typedef int
typedef float
T min(T... args)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
T pow(T... args)
T sin(T... args)
typedef size_t