/src/mozilla-central/dom/media/webaudio/blink/PeriodicWave.cpp
Line | Count | Source (jump to first uncovered line) |
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 <limits> |
33 | | #include "mozilla/FFTBlock.h" |
34 | | |
35 | | const unsigned MinPeriodicWaveSize = 4096; // This must be a power of two. |
36 | | const unsigned MaxPeriodicWaveSize = 8192; // This must be a power of two. |
37 | | const float CentsPerRange = 1200 / 3; // 1/3 Octave. |
38 | | |
39 | | using namespace mozilla; |
40 | | using mozilla::dom::OscillatorType; |
41 | | |
42 | | namespace WebCore { |
43 | | |
44 | | already_AddRefed<PeriodicWave> |
45 | | PeriodicWave::create(float sampleRate, |
46 | | const float* real, |
47 | | const float* imag, |
48 | | size_t numberOfComponents, |
49 | | bool disableNormalization) |
50 | 0 | { |
51 | 0 | bool isGood = real && imag && numberOfComponents > 0; |
52 | 0 | MOZ_ASSERT(isGood); |
53 | 0 | if (isGood) { |
54 | 0 | RefPtr<PeriodicWave> periodicWave = |
55 | 0 | new PeriodicWave(sampleRate, numberOfComponents, |
56 | 0 | disableNormalization); |
57 | 0 |
|
58 | 0 | // Limit the number of components used to those for frequencies below the |
59 | 0 | // Nyquist of the fixed length inverse FFT. |
60 | 0 | size_t halfSize = periodicWave->m_periodicWaveSize / 2; |
61 | 0 | numberOfComponents = std::min(numberOfComponents, halfSize); |
62 | 0 | periodicWave->m_numberOfComponents = numberOfComponents; |
63 | 0 | periodicWave->m_realComponents = new AudioFloatArray(numberOfComponents); |
64 | 0 | periodicWave->m_imagComponents = new AudioFloatArray(numberOfComponents); |
65 | 0 | memcpy(periodicWave->m_realComponents->Elements(), real, |
66 | 0 | numberOfComponents * sizeof(float)); |
67 | 0 | memcpy(periodicWave->m_imagComponents->Elements(), imag, |
68 | 0 | numberOfComponents * sizeof(float)); |
69 | 0 |
|
70 | 0 | return periodicWave.forget(); |
71 | 0 | } |
72 | 0 | return nullptr; |
73 | 0 | } |
74 | | |
75 | | already_AddRefed<PeriodicWave> |
76 | | PeriodicWave::createSine(float sampleRate) |
77 | 0 | { |
78 | 0 | RefPtr<PeriodicWave> periodicWave = |
79 | 0 | new PeriodicWave(sampleRate, MinPeriodicWaveSize, false); |
80 | 0 | periodicWave->generateBasicWaveform(OscillatorType::Sine); |
81 | 0 | return periodicWave.forget(); |
82 | 0 | } |
83 | | |
84 | | already_AddRefed<PeriodicWave> |
85 | | PeriodicWave::createSquare(float sampleRate) |
86 | 0 | { |
87 | 0 | RefPtr<PeriodicWave> periodicWave = |
88 | 0 | new PeriodicWave(sampleRate, MinPeriodicWaveSize, false); |
89 | 0 | periodicWave->generateBasicWaveform(OscillatorType::Square); |
90 | 0 | return periodicWave.forget(); |
91 | 0 | } |
92 | | |
93 | | already_AddRefed<PeriodicWave> |
94 | | PeriodicWave::createSawtooth(float sampleRate) |
95 | 0 | { |
96 | 0 | RefPtr<PeriodicWave> periodicWave = |
97 | 0 | new PeriodicWave(sampleRate, MinPeriodicWaveSize, false); |
98 | 0 | periodicWave->generateBasicWaveform(OscillatorType::Sawtooth); |
99 | 0 | return periodicWave.forget(); |
100 | 0 | } |
101 | | |
102 | | already_AddRefed<PeriodicWave> |
103 | | PeriodicWave::createTriangle(float sampleRate) |
104 | 0 | { |
105 | 0 | RefPtr<PeriodicWave> periodicWave = |
106 | 0 | new PeriodicWave(sampleRate, MinPeriodicWaveSize, false); |
107 | 0 | periodicWave->generateBasicWaveform(OscillatorType::Triangle); |
108 | 0 | return periodicWave.forget(); |
109 | 0 | } |
110 | | |
111 | | PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents, bool disableNormalization) |
112 | | : m_sampleRate(sampleRate) |
113 | | , m_centsPerRange(CentsPerRange) |
114 | | , m_maxPartialsInBandLimitedTable(0) |
115 | | , m_normalizationScale(1.0f) |
116 | | , m_disableNormalization(disableNormalization) |
117 | 0 | { |
118 | 0 | float nyquist = 0.5 * m_sampleRate; |
119 | 0 |
|
120 | 0 | if (numberOfComponents <= MinPeriodicWaveSize) { |
121 | 0 | m_periodicWaveSize = MinPeriodicWaveSize; |
122 | 0 | } else { |
123 | 0 | unsigned npow2 = powf(2.0f, floorf(logf(numberOfComponents - 1.0)/logf(2.0f) + 1.0f)); |
124 | 0 | m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2); |
125 | 0 | } |
126 | 0 |
|
127 | 0 | m_numberOfRanges = (unsigned)(3.0f*logf(m_periodicWaveSize)/logf(2.0f)); |
128 | 0 | m_bandLimitedTables.SetLength(m_numberOfRanges); |
129 | 0 | m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials(); |
130 | 0 | m_rateScale = m_periodicWaveSize / m_sampleRate; |
131 | 0 | } |
132 | | |
133 | | size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
134 | 0 | { |
135 | 0 | size_t amount = aMallocSizeOf(this); |
136 | 0 |
|
137 | 0 | amount += m_bandLimitedTables.ShallowSizeOfExcludingThis(aMallocSizeOf); |
138 | 0 | for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) { |
139 | 0 | if (m_bandLimitedTables[i]) { |
140 | 0 | amount += m_bandLimitedTables[i]->ShallowSizeOfIncludingThis(aMallocSizeOf); |
141 | 0 | } |
142 | 0 | } |
143 | 0 |
|
144 | 0 | return amount; |
145 | 0 | } |
146 | | |
147 | | void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor) |
148 | 0 | { |
149 | 0 |
|
150 | 0 | // Negative frequencies are allowed, in which case we alias |
151 | 0 | // to the positive frequency. |
152 | 0 | fundamentalFrequency = fabsf(fundamentalFrequency); |
153 | 0 |
|
154 | 0 | // We only need to rebuild to the tables if the new fundamental |
155 | 0 | // frequency is low enough to allow for more partials below the |
156 | 0 | // Nyquist frequency. |
157 | 0 | unsigned numberOfPartials = numberOfPartialsForRange(0); |
158 | 0 | float nyquist = 0.5 * m_sampleRate; |
159 | 0 | if (fundamentalFrequency != 0.0) { |
160 | 0 | numberOfPartials = std::min(numberOfPartials, (unsigned)(nyquist / fundamentalFrequency)); |
161 | 0 | } |
162 | 0 | if (numberOfPartials > m_maxPartialsInBandLimitedTable) { |
163 | 0 | for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { |
164 | 0 | m_bandLimitedTables[rangeIndex] = 0; |
165 | 0 | } |
166 | 0 |
|
167 | 0 | // We need to create the first table to determine the normalization |
168 | 0 | // constant. |
169 | 0 | createBandLimitedTables(fundamentalFrequency, 0); |
170 | 0 | m_maxPartialsInBandLimitedTable = numberOfPartials; |
171 | 0 | } |
172 | 0 |
|
173 | 0 | // Calculate the pitch range. |
174 | 0 | float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5; |
175 | 0 | float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200; |
176 | 0 |
|
177 | 0 | // Add one to round-up to the next range just in time to truncate |
178 | 0 | // partials before aliasing occurs. |
179 | 0 | float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange; |
180 | 0 |
|
181 | 0 | pitchRange = std::max(pitchRange, 0.0f); |
182 | 0 | pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1)); |
183 | 0 |
|
184 | 0 | // The words "lower" and "higher" refer to the table data having |
185 | 0 | // the lower and higher numbers of partials. It's a little confusing |
186 | 0 | // since the range index gets larger the more partials we cull out. |
187 | 0 | // So the lower table data will have a larger range index. |
188 | 0 | unsigned rangeIndex1 = static_cast<unsigned>(pitchRange); |
189 | 0 | unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1; |
190 | 0 |
|
191 | 0 | if (!m_bandLimitedTables[rangeIndex1].get()) |
192 | 0 | createBandLimitedTables(fundamentalFrequency, rangeIndex1); |
193 | 0 |
|
194 | 0 | if (!m_bandLimitedTables[rangeIndex2].get()) |
195 | 0 | createBandLimitedTables(fundamentalFrequency, rangeIndex2); |
196 | 0 |
|
197 | 0 | lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements(); |
198 | 0 | higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements(); |
199 | 0 |
|
200 | 0 | // Ranges from 0 -> 1 to interpolate between lower -> higher. |
201 | 0 | tableInterpolationFactor = rangeIndex2 - pitchRange; |
202 | 0 | } |
203 | | |
204 | | unsigned PeriodicWave::maxNumberOfPartials() const |
205 | 0 | { |
206 | 0 | return m_periodicWaveSize / 2; |
207 | 0 | } |
208 | | |
209 | | unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const |
210 | 0 | { |
211 | 0 | // Number of cents below nyquist where we cull partials. |
212 | 0 | float centsToCull = rangeIndex * m_centsPerRange; |
213 | 0 |
|
214 | 0 | // A value from 0 -> 1 representing what fraction of the partials to keep. |
215 | 0 | float cullingScale = pow(2, -centsToCull / 1200); |
216 | 0 |
|
217 | 0 | // The very top range will have all the partials culled. |
218 | 0 | unsigned numberOfPartials = cullingScale * maxNumberOfPartials(); |
219 | 0 |
|
220 | 0 | return numberOfPartials; |
221 | 0 | } |
222 | | |
223 | | // Convert into time-domain wave buffers. |
224 | | // One table is created for each range for non-aliasing playback |
225 | | // at different playback rates. Thus, higher ranges have more |
226 | | // high-frequency partials culled out. |
227 | | void PeriodicWave::createBandLimitedTables(float fundamentalFrequency, |
228 | | unsigned rangeIndex) |
229 | 0 | { |
230 | 0 | unsigned fftSize = m_periodicWaveSize; |
231 | 0 | unsigned i; |
232 | 0 |
|
233 | 0 | const float *realData = m_realComponents->Elements(); |
234 | 0 | const float *imagData = m_imagComponents->Elements(); |
235 | 0 |
|
236 | 0 | // This FFTBlock is used to cull partials (represented by frequency bins). |
237 | 0 | FFTBlock frame(fftSize); |
238 | 0 |
|
239 | 0 | // Find the starting bin where we should start culling the aliasing |
240 | 0 | // partials for this pitch range. We need to clear out the highest |
241 | 0 | // frequencies to band-limit the waveform. |
242 | 0 | unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); |
243 | 0 | // Also limit to the number of components that are provided. |
244 | 0 | numberOfPartials = std::min(numberOfPartials, m_numberOfComponents - 1); |
245 | 0 |
|
246 | 0 | // Limit number of partials to those below Nyquist frequency |
247 | 0 | float nyquist = 0.5 * m_sampleRate; |
248 | 0 | if (fundamentalFrequency != 0.0) { |
249 | 0 | numberOfPartials = std::min(numberOfPartials, |
250 | 0 | (unsigned)(nyquist / fundamentalFrequency)); |
251 | 0 | } |
252 | 0 |
|
253 | 0 | // Copy from loaded frequency data and generate complex conjugate |
254 | 0 | // because of the way the inverse FFT is defined. |
255 | 0 | // The coefficients of higher partials remain zero, as initialized in |
256 | 0 | // the FFTBlock constructor. |
257 | 0 | for (i = 0; i < numberOfPartials + 1; ++i) { |
258 | 0 | frame.RealData(i) = realData[i]; |
259 | 0 | frame.ImagData(i) = -imagData[i]; |
260 | 0 | } |
261 | 0 |
|
262 | 0 | // Clear any DC-offset. |
263 | 0 | frame.RealData(0) = 0; |
264 | 0 | // Clear value which has no effect. |
265 | 0 | frame.ImagData(0) = 0; |
266 | 0 |
|
267 | 0 | // Create the band-limited table. |
268 | 0 | AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize); |
269 | 0 | m_bandLimitedTables[rangeIndex] = table; |
270 | 0 |
|
271 | 0 | // Apply an inverse FFT to generate the time-domain table data. |
272 | 0 | float* data = m_bandLimitedTables[rangeIndex]->Elements(); |
273 | 0 | frame.GetInverseWithoutScaling(data); |
274 | 0 |
|
275 | 0 | // For the first range (which has the highest power), calculate |
276 | 0 | // its peak value then compute normalization scale. |
277 | 0 | if (m_disableNormalization) { |
278 | 0 | // See Bug 1424906, results need to be scaled by 0.5 even |
279 | 0 | // when normalization is disabled. |
280 | 0 | m_normalizationScale = 0.5; |
281 | 0 | } else if (!rangeIndex) { |
282 | 0 | float maxValue; |
283 | 0 | maxValue = AudioBufferPeakValue(data, m_periodicWaveSize); |
284 | 0 |
|
285 | 0 | if (maxValue) |
286 | 0 | m_normalizationScale = 1.0f / maxValue; |
287 | 0 | } |
288 | 0 |
|
289 | 0 | // Apply normalization scale. |
290 | 0 | AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize); |
291 | 0 | } |
292 | | |
293 | | void PeriodicWave::generateBasicWaveform(OscillatorType shape) |
294 | 0 | { |
295 | 0 | const float piFloat = float(M_PI); |
296 | 0 | unsigned fftSize = periodicWaveSize(); |
297 | 0 | unsigned halfSize = fftSize / 2; |
298 | 0 |
|
299 | 0 | m_numberOfComponents = halfSize; |
300 | 0 | m_realComponents = new AudioFloatArray(halfSize); |
301 | 0 | m_imagComponents = new AudioFloatArray(halfSize); |
302 | 0 | float* realP = m_realComponents->Elements(); |
303 | 0 | float* imagP = m_imagComponents->Elements(); |
304 | 0 |
|
305 | 0 | // Clear DC and imag value which is ignored. |
306 | 0 | realP[0] = 0; |
307 | 0 | imagP[0] = 0; |
308 | 0 |
|
309 | 0 | for (unsigned n = 1; n < halfSize; ++n) { |
310 | 0 | float omega = 2 * piFloat * n; |
311 | 0 | float invOmega = 1 / omega; |
312 | 0 |
|
313 | 0 | // Fourier coefficients according to standard definition. |
314 | 0 | float a; // Coefficient for cos(). |
315 | 0 | float b; // Coefficient for sin(). |
316 | 0 |
|
317 | 0 | // Calculate Fourier coefficients depending on the shape. |
318 | 0 | // Note that the overall scaling (magnitude) of the waveforms |
319 | 0 | // is normalized in createBandLimitedTables(). |
320 | 0 | switch (shape) { |
321 | 0 | case OscillatorType::Sine: |
322 | 0 | // Standard sine wave function. |
323 | 0 | a = 0; |
324 | 0 | b = (n == 1) ? 1 : 0; |
325 | 0 | break; |
326 | 0 | case OscillatorType::Square: |
327 | 0 | // Square-shaped waveform with the first half its maximum value |
328 | 0 | // and the second half its minimum value. |
329 | 0 | a = 0; |
330 | 0 | b = invOmega * ((n & 1) ? 2 : 0); |
331 | 0 | break; |
332 | 0 | case OscillatorType::Sawtooth: |
333 | 0 | // Sawtooth-shaped waveform with the first half ramping from |
334 | 0 | // zero to maximum and the second half from minimum to zero. |
335 | 0 | a = 0; |
336 | 0 | b = -invOmega * cos(0.5 * omega); |
337 | 0 | break; |
338 | 0 | case OscillatorType::Triangle: |
339 | 0 | // Triangle-shaped waveform going from its maximum value to |
340 | 0 | // its minimum value then back to the maximum value. |
341 | 0 | a = 0; |
342 | 0 | if (n & 1) { |
343 | 0 | b = 2 * (2 / (n * piFloat) * 2 / (n * piFloat)) * ((((n - 1) >> 1) & 1) ? -1 : 1); |
344 | 0 | } else { |
345 | 0 | b = 0; |
346 | 0 | } |
347 | 0 | break; |
348 | 0 | default: |
349 | 0 | MOZ_ASSERT_UNREACHABLE("invalid oscillator type"); |
350 | 0 | a = 0; |
351 | 0 | b = 0; |
352 | 0 | break; |
353 | 0 | } |
354 | 0 |
|
355 | 0 | realP[n] = a; |
356 | 0 | imagP[n] = b; |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | | } // namespace WebCore |