content/media/webaudio/blink/PeriodicWave.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:81e8d09231c6
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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "PeriodicWave.h"
30 #include <algorithm>
31 #include <cmath>
32 #include "mozilla/FFTBlock.h"
33
34 const unsigned PeriodicWaveSize = 4096; // This must be a power of two.
35 const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges.
36 const float CentsPerRange = 1200 / 3; // 1/3 Octave.
37
38 using namespace mozilla;
39 using mozilla::dom::OscillatorType;
40
41 namespace WebCore {
42
43 PeriodicWave* PeriodicWave::create(float sampleRate,
44 const float* real,
45 const float* imag,
46 size_t numberOfComponents)
47 {
48 bool isGood = real && imag && numberOfComponents > 0 &&
49 numberOfComponents <= PeriodicWaveSize;
50 MOZ_ASSERT(isGood);
51 if (isGood) {
52 PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
53 periodicWave->createBandLimitedTables(real, imag, numberOfComponents);
54 return periodicWave;
55 }
56 return 0;
57 }
58
59 PeriodicWave* PeriodicWave::createSine(float sampleRate)
60 {
61 PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
62 periodicWave->generateBasicWaveform(OscillatorType::Sine);
63 return periodicWave;
64 }
65
66 PeriodicWave* PeriodicWave::createSquare(float sampleRate)
67 {
68 PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
69 periodicWave->generateBasicWaveform(OscillatorType::Square);
70 return periodicWave;
71 }
72
73 PeriodicWave* PeriodicWave::createSawtooth(float sampleRate)
74 {
75 PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
76 periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
77 return periodicWave;
78 }
79
80 PeriodicWave* PeriodicWave::createTriangle(float sampleRate)
81 {
82 PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
83 periodicWave->generateBasicWaveform(OscillatorType::Triangle);
84 return periodicWave;
85 }
86
87 PeriodicWave::PeriodicWave(float sampleRate)
88 : m_sampleRate(sampleRate)
89 , m_periodicWaveSize(PeriodicWaveSize)
90 , m_numberOfRanges(NumberOfRanges)
91 , m_centsPerRange(CentsPerRange)
92 {
93 float nyquist = 0.5 * m_sampleRate;
94 m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
95 m_rateScale = m_periodicWaveSize / m_sampleRate;
96 }
97
98 size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
99 {
100 size_t amount = aMallocSizeOf(this);
101
102 amount += m_bandLimitedTables.SizeOfExcludingThis(aMallocSizeOf);
103 for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) {
104 if (m_bandLimitedTables[i]) {
105 amount += m_bandLimitedTables[i]->SizeOfIncludingThis(aMallocSizeOf);
106 }
107 }
108
109 return amount;
110 }
111
112 void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
113 {
114 // Negative frequencies are allowed, in which case we alias
115 // to the positive frequency.
116 fundamentalFrequency = fabsf(fundamentalFrequency);
117
118 // Calculate the pitch range.
119 float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
120 float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
121
122 // Add one to round-up to the next range just in time to truncate
123 // partials before aliasing occurs.
124 float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
125
126 pitchRange = std::max(pitchRange, 0.0f);
127 pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1));
128
129 // The words "lower" and "higher" refer to the table data having
130 // the lower and higher numbers of partials. It's a little confusing
131 // since the range index gets larger the more partials we cull out.
132 // So the lower table data will have a larger range index.
133 unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
134 unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
135
136 lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
137 higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
138
139 // Ranges from 0 -> 1 to interpolate between lower -> higher.
140 tableInterpolationFactor = pitchRange - rangeIndex1;
141 }
142
143 unsigned PeriodicWave::maxNumberOfPartials() const
144 {
145 return m_periodicWaveSize / 2;
146 }
147
148 unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
149 {
150 // Number of cents below nyquist where we cull partials.
151 float centsToCull = rangeIndex * m_centsPerRange;
152
153 // A value from 0 -> 1 representing what fraction of the partials to keep.
154 float cullingScale = pow(2, -centsToCull / 1200);
155
156 // The very top range will have all the partials culled.
157 unsigned numberOfPartials = cullingScale * maxNumberOfPartials();
158
159 return numberOfPartials;
160 }
161
162 // Convert into time-domain wave buffers.
163 // One table is created for each range for non-aliasing playback
164 // at different playback rates. Thus, higher ranges have more
165 // high-frequency partials culled out.
166 void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
167 {
168 float normalizationScale = 1;
169
170 unsigned fftSize = m_periodicWaveSize;
171 unsigned halfSize = fftSize / 2 + 1;
172 unsigned i;
173
174 numberOfComponents = std::min(numberOfComponents, halfSize);
175
176 m_bandLimitedTables.SetCapacity(m_numberOfRanges);
177
178 for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
179 // This FFTBlock is used to cull partials (represented by frequency bins).
180 FFTBlock frame(fftSize);
181 nsAutoArrayPtr<float> realP(new float[halfSize]);
182 nsAutoArrayPtr<float> imagP(new float[halfSize]);
183
184 // Copy from loaded frequency data and scale.
185 float scale = fftSize;
186 AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents);
187 AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents);
188
189 // If fewer components were provided than 1/2 FFT size,
190 // then clear the remaining bins.
191 for (i = numberOfComponents; i < halfSize; ++i) {
192 realP[i] = 0;
193 imagP[i] = 0;
194 }
195
196 // Generate complex conjugate because of the way the
197 // inverse FFT is defined.
198 float minusOne = -1;
199 AudioBufferInPlaceScale(imagP, minusOne, halfSize);
200
201 // Find the starting bin where we should start culling.
202 // We need to clear out the highest frequencies to band-limit
203 // the waveform.
204 unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
205
206 // Cull the aliasing partials for this pitch range.
207 for (i = numberOfPartials + 1; i < halfSize; ++i) {
208 realP[i] = 0;
209 imagP[i] = 0;
210 }
211 // Clear nyquist if necessary.
212 if (numberOfPartials < halfSize)
213 realP[halfSize-1] = 0;
214
215 // Clear any DC-offset.
216 realP[0] = 0;
217
218 // Clear values which have no effect.
219 imagP[0] = 0;
220 imagP[halfSize-1] = 0;
221
222 // Create the band-limited table.
223 AudioFloatArray* table = new AudioFloatArray(m_periodicWaveSize);
224 m_bandLimitedTables.AppendElement(table);
225
226 // Apply an inverse FFT to generate the time-domain table data.
227 float* data = m_bandLimitedTables[rangeIndex]->Elements();
228 frame.PerformInverseFFT(realP, imagP, data);
229
230 // For the first range (which has the highest power), calculate
231 // its peak value then compute normalization scale.
232 if (!rangeIndex) {
233 float maxValue;
234 maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
235
236 if (maxValue)
237 normalizationScale = 1.0f / maxValue;
238 }
239
240 // Apply normalization scale.
241 AudioBufferInPlaceScale(data, normalizationScale, m_periodicWaveSize);
242 }
243 }
244
245 void PeriodicWave::generateBasicWaveform(OscillatorType shape)
246 {
247 const float piFloat = M_PI;
248 unsigned fftSize = periodicWaveSize();
249 unsigned halfSize = fftSize / 2 + 1;
250
251 AudioFloatArray real(halfSize);
252 AudioFloatArray imag(halfSize);
253 float* realP = real.Elements();
254 float* imagP = imag.Elements();
255
256 // Clear DC and Nyquist.
257 realP[0] = 0;
258 imagP[0] = 0;
259 realP[halfSize-1] = 0;
260 imagP[halfSize-1] = 0;
261
262 for (unsigned n = 1; n < halfSize; ++n) {
263 float omega = 2 * piFloat * n;
264 float invOmega = 1 / omega;
265
266 // Fourier coefficients according to standard definition.
267 float a; // Coefficient for cos().
268 float b; // Coefficient for sin().
269
270 // Calculate Fourier coefficients depending on the shape.
271 // Note that the overall scaling (magnitude) of the waveforms
272 // is normalized in createBandLimitedTables().
273 switch (shape) {
274 case OscillatorType::Sine:
275 // Standard sine wave function.
276 a = 0;
277 b = (n == 1) ? 1 : 0;
278 break;
279 case OscillatorType::Square:
280 // Square-shaped waveform with the first half its maximum value
281 // and the second half its minimum value.
282 a = 0;
283 b = invOmega * ((n & 1) ? 2 : 0);
284 break;
285 case OscillatorType::Sawtooth:
286 // Sawtooth-shaped waveform with the first half ramping from
287 // zero to maximum and the second half from minimum to zero.
288 a = 0;
289 b = -invOmega * cos(0.5 * omega);
290 break;
291 case OscillatorType::Triangle:
292 // Triangle-shaped waveform going from its maximum value to
293 // its minimum value then back to the maximum value.
294 a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat);
295 b = 0;
296 break;
297 default:
298 NS_NOTREACHED("invalid oscillator type");
299 a = 0;
300 b = 0;
301 break;
302 }
303
304 realP[n] = a;
305 imagP[n] = b;
306 }
307
308 createBandLimitedTables(realP, imagP, halfSize);
309 }
310
311 } // namespace WebCore

mercurial