|
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 |