1/*
2 * Copyright (C) 2012, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26
27#if ENABLE(WEB_AUDIO)
28
29#include "OscillatorNode.h"
30
31#include "AudioNodeOutput.h"
32#include "AudioParam.h"
33#include "PeriodicWave.h"
34#include "VectorMath.h"
35#include <wtf/IsoMallocInlines.h>
36
37namespace WebCore {
38
39using namespace VectorMath;
40
41WTF_MAKE_ISO_ALLOCATED_IMPL(OscillatorNode);
42
43PeriodicWave* OscillatorNode::s_periodicWaveSine = nullptr;
44PeriodicWave* OscillatorNode::s_periodicWaveSquare = nullptr;
45PeriodicWave* OscillatorNode::s_periodicWaveSawtooth = nullptr;
46PeriodicWave* OscillatorNode::s_periodicWaveTriangle = nullptr;
47
48Ref<OscillatorNode> OscillatorNode::create(AudioContext& context, float sampleRate)
49{
50 return adoptRef(*new OscillatorNode(context, sampleRate));
51}
52
53OscillatorNode::OscillatorNode(AudioContext& context, float sampleRate)
54 : AudioScheduledSourceNode(context, sampleRate)
55 , m_firstRender(true)
56 , m_virtualReadIndex(0)
57 , m_phaseIncrements(AudioNode::ProcessingSizeInFrames)
58 , m_detuneValues(AudioNode::ProcessingSizeInFrames)
59{
60 setNodeType(NodeTypeOscillator);
61
62 // Use musical pitch standard A440 as a default.
63 m_frequency = AudioParam::create(context, "frequency", 440, 0, 100000);
64 // Default to no detuning.
65 m_detune = AudioParam::create(context, "detune", 0, -4800, 4800);
66
67 // Sets up default wave.
68 setType(m_type);
69
70 // An oscillator is always mono.
71 addOutput(std::make_unique<AudioNodeOutput>(this, 1));
72
73 initialize();
74}
75
76OscillatorNode::~OscillatorNode()
77{
78 uninitialize();
79}
80
81ExceptionOr<void> OscillatorNode::setType(Type type)
82{
83 PeriodicWave* periodicWave = nullptr;
84
85 ALWAYS_LOG(LOGIDENTIFIER, type);
86
87 switch (type) {
88 case Type::Sine:
89 if (!s_periodicWaveSine)
90 s_periodicWaveSine = &PeriodicWave::createSine(sampleRate()).leakRef();
91 periodicWave = s_periodicWaveSine;
92 break;
93 case Type::Square:
94 if (!s_periodicWaveSquare)
95 s_periodicWaveSquare = &PeriodicWave::createSquare(sampleRate()).leakRef();
96 periodicWave = s_periodicWaveSquare;
97 break;
98 case Type::Sawtooth:
99 if (!s_periodicWaveSawtooth)
100 s_periodicWaveSawtooth = &PeriodicWave::createSawtooth(sampleRate()).leakRef();
101 periodicWave = s_periodicWaveSawtooth;
102 break;
103 case Type::Triangle:
104 if (!s_periodicWaveTriangle)
105 s_periodicWaveTriangle = &PeriodicWave::createTriangle(sampleRate()).leakRef();
106 periodicWave = s_periodicWaveTriangle;
107 break;
108 case Type::Custom:
109 if (m_type != Type::Custom)
110 return Exception { InvalidStateError };
111 return { };
112 }
113
114 setPeriodicWave(periodicWave);
115 m_type = type;
116
117 return { };
118}
119
120bool OscillatorNode::calculateSampleAccuratePhaseIncrements(size_t framesToProcess)
121{
122 bool isGood = framesToProcess <= m_phaseIncrements.size() && framesToProcess <= m_detuneValues.size();
123 ASSERT(isGood);
124 if (!isGood)
125 return false;
126
127 if (m_firstRender) {
128 m_firstRender = false;
129 m_frequency->resetSmoothedValue();
130 m_detune->resetSmoothedValue();
131 }
132
133 bool hasSampleAccurateValues = false;
134 bool hasFrequencyChanges = false;
135 float* phaseIncrements = m_phaseIncrements.data();
136
137 float finalScale = m_periodicWave->rateScale();
138
139 if (m_frequency->hasSampleAccurateValues()) {
140 hasSampleAccurateValues = true;
141 hasFrequencyChanges = true;
142
143 // Get the sample-accurate frequency values and convert to phase increments.
144 // They will be converted to phase increments below.
145 m_frequency->calculateSampleAccurateValues(phaseIncrements, framesToProcess);
146 } else {
147 // Handle ordinary parameter smoothing/de-zippering if there are no scheduled changes.
148 m_frequency->smooth();
149 float frequency = m_frequency->smoothedValue();
150 finalScale *= frequency;
151 }
152
153 if (m_detune->hasSampleAccurateValues()) {
154 hasSampleAccurateValues = true;
155
156 // Get the sample-accurate detune values.
157 float* detuneValues = hasFrequencyChanges ? m_detuneValues.data() : phaseIncrements;
158 m_detune->calculateSampleAccurateValues(detuneValues, framesToProcess);
159
160 // Convert from cents to rate scalar.
161 float k = 1.0 / 1200;
162 vsmul(detuneValues, 1, &k, detuneValues, 1, framesToProcess);
163 for (unsigned i = 0; i < framesToProcess; ++i)
164 detuneValues[i] = powf(2, detuneValues[i]); // FIXME: converting to expf() will be faster.
165
166 if (hasFrequencyChanges) {
167 // Multiply frequencies by detune scalings.
168 vmul(detuneValues, 1, phaseIncrements, 1, phaseIncrements, 1, framesToProcess);
169 }
170 } else {
171 // Handle ordinary parameter smoothing/de-zippering if there are no scheduled changes.
172 m_detune->smooth();
173 float detune = m_detune->smoothedValue();
174 float detuneScale = powf(2, detune / 1200);
175 finalScale *= detuneScale;
176 }
177
178 if (hasSampleAccurateValues) {
179 // Convert from frequency to wave increment.
180 vsmul(phaseIncrements, 1, &finalScale, phaseIncrements, 1, framesToProcess);
181 }
182
183 return hasSampleAccurateValues;
184}
185
186void OscillatorNode::process(size_t framesToProcess)
187{
188 auto& outputBus = *output(0)->bus();
189
190 if (!isInitialized() || !outputBus.numberOfChannels()) {
191 outputBus.zero();
192 return;
193 }
194
195 ASSERT(framesToProcess <= m_phaseIncrements.size());
196 if (framesToProcess > m_phaseIncrements.size())
197 return;
198
199 // The audio thread can't block on this lock, so we use std::try_to_lock instead.
200 std::unique_lock<Lock> lock(m_processMutex, std::try_to_lock);
201 if (!lock.owns_lock()) {
202 // Too bad - the try_lock() failed. We must be in the middle of changing wave-tables.
203 outputBus.zero();
204 return;
205 }
206
207 // We must access m_periodicWave only inside the lock.
208 if (!m_periodicWave.get()) {
209 outputBus.zero();
210 return;
211 }
212
213 size_t quantumFrameOffset = 0;
214 size_t nonSilentFramesToProcess = 0;
215 updateSchedulingInfo(framesToProcess, outputBus, quantumFrameOffset, nonSilentFramesToProcess);
216
217 if (!nonSilentFramesToProcess) {
218 outputBus.zero();
219 return;
220 }
221
222 unsigned periodicWaveSize = m_periodicWave->periodicWaveSize();
223 double invPeriodicWaveSize = 1.0 / periodicWaveSize;
224
225 float* destP = outputBus.channel(0)->mutableData();
226
227 ASSERT(quantumFrameOffset <= framesToProcess);
228
229 // We keep virtualReadIndex double-precision since we're accumulating values.
230 double virtualReadIndex = m_virtualReadIndex;
231
232 float rateScale = m_periodicWave->rateScale();
233 float invRateScale = 1 / rateScale;
234 bool hasSampleAccurateValues = calculateSampleAccuratePhaseIncrements(framesToProcess);
235
236 float frequency = 0;
237 float* higherWaveData = nullptr;
238 float* lowerWaveData = nullptr;
239 float tableInterpolationFactor = 0;
240
241 if (!hasSampleAccurateValues) {
242 frequency = m_frequency->smoothedValue();
243 float detune = m_detune->smoothedValue();
244 float detuneScale = powf(2, detune / 1200);
245 frequency *= detuneScale;
246 m_periodicWave->waveDataForFundamentalFrequency(frequency, lowerWaveData, higherWaveData, tableInterpolationFactor);
247 }
248
249 float incr = frequency * rateScale;
250 float* phaseIncrements = m_phaseIncrements.data();
251
252 unsigned readIndexMask = periodicWaveSize - 1;
253
254 // Start rendering at the correct offset.
255 destP += quantumFrameOffset;
256 int n = nonSilentFramesToProcess;
257
258 while (n--) {
259 unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
260 unsigned readIndex2 = readIndex + 1;
261
262 // Contain within valid range.
263 readIndex = readIndex & readIndexMask;
264 readIndex2 = readIndex2 & readIndexMask;
265
266 if (hasSampleAccurateValues) {
267 incr = *phaseIncrements++;
268
269 frequency = invRateScale * incr;
270 m_periodicWave->waveDataForFundamentalFrequency(frequency, lowerWaveData, higherWaveData, tableInterpolationFactor);
271 }
272
273 float sample1Lower = lowerWaveData[readIndex];
274 float sample2Lower = lowerWaveData[readIndex2];
275 float sample1Higher = higherWaveData[readIndex];
276 float sample2Higher = higherWaveData[readIndex2];
277
278 // Linearly interpolate within each table (lower and higher).
279 float interpolationFactor = static_cast<float>(virtualReadIndex) - readIndex;
280 float sampleHigher = (1 - interpolationFactor) * sample1Higher + interpolationFactor * sample2Higher;
281 float sampleLower = (1 - interpolationFactor) * sample1Lower + interpolationFactor * sample2Lower;
282
283 // Then interpolate between the two tables.
284 float sample = (1 - tableInterpolationFactor) * sampleHigher + tableInterpolationFactor * sampleLower;
285
286 *destP++ = sample;
287
288 // Increment virtual read index and wrap virtualReadIndex into the range 0 -> periodicWaveSize.
289 virtualReadIndex += incr;
290 virtualReadIndex -= floor(virtualReadIndex * invPeriodicWaveSize) * periodicWaveSize;
291 }
292
293 m_virtualReadIndex = virtualReadIndex;
294
295 outputBus.clearSilentFlag();
296}
297
298void OscillatorNode::reset()
299{
300 m_virtualReadIndex = 0;
301}
302
303void OscillatorNode::setPeriodicWave(PeriodicWave* periodicWave)
304{
305 ALWAYS_LOG(LOGIDENTIFIER, "sample rate = ", periodicWave ? periodicWave->sampleRate() : 0, ", wave size = ", periodicWave ? periodicWave->periodicWaveSize() : 0, ", rate scale = ", periodicWave ? periodicWave->rateScale() : 0);
306 ASSERT(isMainThread());
307
308 // This synchronizes with process().
309 std::lock_guard<Lock> lock(m_processMutex);
310 m_periodicWave = periodicWave;
311 m_type = Type::Custom;
312}
313
314bool OscillatorNode::propagatesSilence() const
315{
316 return !isPlayingOrScheduled() || hasFinished() || !m_periodicWave.get();
317}
318
319} // namespace WebCore
320
321#endif // ENABLE(WEB_AUDIO)
322