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 "config.h" 30 31 #if ENABLE(WEB_AUDIO) 32 33 #include "modules/webaudio/PeriodicWave.h" 34 35 #include "core/platform/audio/FFTFrame.h" 36 #include "core/platform/audio/VectorMath.h" 37 #include "modules/webaudio/OscillatorNode.h" 38 #include "wtf/OwnPtr.h" 39 #include <algorithm> 40 41 const unsigned PeriodicWaveSize = 4096; // This must be a power of two. 42 const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges. 43 const float CentsPerRange = 1200 / 3; // 1/3 Octave. 44 45 namespace WebCore { 46 47 using namespace VectorMath; 48 49 PassRefPtr<PeriodicWave> PeriodicWave::create(float sampleRate, Float32Array* real, Float32Array* imag) 50 { 51 bool isGood = real && imag && real->length() == imag->length(); 52 ASSERT(isGood); 53 if (isGood) { 54 RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate)); 55 size_t numberOfComponents = real->length(); 56 periodicWave->createBandLimitedTables(real->data(), imag->data(), numberOfComponents); 57 return periodicWave; 58 } 59 return 0; 60 } 61 62 PassRefPtr<PeriodicWave> PeriodicWave::createSine(float sampleRate) 63 { 64 RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate)); 65 periodicWave->generateBasicWaveform(OscillatorNode::SINE); 66 return periodicWave; 67 } 68 69 PassRefPtr<PeriodicWave> PeriodicWave::createSquare(float sampleRate) 70 { 71 RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate)); 72 periodicWave->generateBasicWaveform(OscillatorNode::SQUARE); 73 return periodicWave; 74 } 75 76 PassRefPtr<PeriodicWave> PeriodicWave::createSawtooth(float sampleRate) 77 { 78 RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate)); 79 periodicWave->generateBasicWaveform(OscillatorNode::SAWTOOTH); 80 return periodicWave; 81 } 82 83 PassRefPtr<PeriodicWave> PeriodicWave::createTriangle(float sampleRate) 84 { 85 RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate)); 86 periodicWave->generateBasicWaveform(OscillatorNode::TRIANGLE); 87 return periodicWave; 88 } 89 90 PeriodicWave::PeriodicWave(float sampleRate) 91 : m_sampleRate(sampleRate) 92 , m_periodicWaveSize(PeriodicWaveSize) 93 , m_numberOfRanges(NumberOfRanges) 94 , m_centsPerRange(CentsPerRange) 95 { 96 ScriptWrappable::init(this); 97 float nyquist = 0.5 * m_sampleRate; 98 m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials(); 99 m_rateScale = m_periodicWaveSize / m_sampleRate; 100 } 101 102 void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor) 103 { 104 // Negative frequencies are allowed, in which case we alias to the positive frequency. 105 fundamentalFrequency = fabsf(fundamentalFrequency); 106 107 // Calculate the pitch range. 108 float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5; 109 float centsAboveLowestFrequency = log2f(ratio) * 1200; 110 111 // Add one to round-up to the next range just in time to truncate partials before aliasing occurs. 112 float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange; 113 114 pitchRange = std::max(pitchRange, 0.0f); 115 pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1)); 116 117 // The words "lower" and "higher" refer to the table data having the lower and higher numbers of partials. 118 // It's a little confusing since the range index gets larger the more partials we cull out. 119 // So the lower table data will have a larger range index. 120 unsigned rangeIndex1 = static_cast<unsigned>(pitchRange); 121 unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1; 122 123 lowerWaveData = m_bandLimitedTables[rangeIndex2]->data(); 124 higherWaveData = m_bandLimitedTables[rangeIndex1]->data(); 125 126 // Ranges from 0 -> 1 to interpolate between lower -> higher. 127 tableInterpolationFactor = pitchRange - rangeIndex1; 128 } 129 130 unsigned PeriodicWave::maxNumberOfPartials() const 131 { 132 return m_periodicWaveSize / 2; 133 } 134 135 unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const 136 { 137 // Number of cents below nyquist where we cull partials. 138 float centsToCull = rangeIndex * m_centsPerRange; 139 140 // A value from 0 -> 1 representing what fraction of the partials to keep. 141 float cullingScale = pow(2, -centsToCull / 1200); 142 143 // The very top range will have all the partials culled. 144 unsigned numberOfPartials = cullingScale * maxNumberOfPartials(); 145 146 return numberOfPartials; 147 } 148 149 // Convert into time-domain wave buffers. 150 // One table is created for each range for non-aliasing playback at different playback rates. 151 // Thus, higher ranges have more high-frequency partials culled out. 152 void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents) 153 { 154 float normalizationScale = 1; 155 156 unsigned fftSize = m_periodicWaveSize; 157 unsigned halfSize = fftSize / 2; 158 unsigned i; 159 160 numberOfComponents = std::min(numberOfComponents, halfSize); 161 162 m_bandLimitedTables.reserveCapacity(m_numberOfRanges); 163 164 for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { 165 // This FFTFrame is used to cull partials (represented by frequency bins). 166 FFTFrame frame(fftSize); 167 float* realP = frame.realData(); 168 float* imagP = frame.imagData(); 169 170 // Copy from loaded frequency data and scale. 171 float scale = fftSize; 172 vsmul(realData, 1, &scale, realP, 1, numberOfComponents); 173 vsmul(imagData, 1, &scale, imagP, 1, numberOfComponents); 174 175 // If fewer components were provided than 1/2 FFT size, then clear the remaining bins. 176 for (i = numberOfComponents; i < halfSize; ++i) { 177 realP[i] = 0; 178 imagP[i] = 0; 179 } 180 181 // Generate complex conjugate because of the way the inverse FFT is defined. 182 float minusOne = -1; 183 vsmul(imagP, 1, &minusOne, imagP, 1, halfSize); 184 185 // Find the starting bin where we should start culling. 186 // We need to clear out the highest frequencies to band-limit the waveform. 187 unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); 188 189 // Cull the aliasing partials for this pitch range. 190 for (i = numberOfPartials + 1; i < halfSize; ++i) { 191 realP[i] = 0; 192 imagP[i] = 0; 193 } 194 // Clear packed-nyquist if necessary. 195 if (numberOfPartials < halfSize) 196 imagP[0] = 0; 197 198 // Clear any DC-offset. 199 realP[0] = 0; 200 201 // Create the band-limited table. 202 OwnPtr<AudioFloatArray> table = adoptPtr(new AudioFloatArray(m_periodicWaveSize)); 203 m_bandLimitedTables.append(table.release()); 204 205 // Apply an inverse FFT to generate the time-domain table data. 206 float* data = m_bandLimitedTables[rangeIndex]->data(); 207 frame.doInverseFFT(data); 208 209 // For the first range (which has the highest power), calculate its peak value then compute normalization scale. 210 if (!rangeIndex) { 211 float maxValue; 212 vmaxmgv(data, 1, &maxValue, m_periodicWaveSize); 213 214 if (maxValue) 215 normalizationScale = 1.0f / maxValue; 216 } 217 218 // Apply normalization scale. 219 vsmul(data, 1, &normalizationScale, data, 1, m_periodicWaveSize); 220 } 221 } 222 223 void PeriodicWave::generateBasicWaveform(int shape) 224 { 225 unsigned fftSize = periodicWaveSize(); 226 unsigned halfSize = fftSize / 2; 227 228 AudioFloatArray real(halfSize); 229 AudioFloatArray imag(halfSize); 230 float* realP = real.data(); 231 float* imagP = imag.data(); 232 233 // Clear DC and Nyquist. 234 realP[0] = 0; 235 imagP[0] = 0; 236 237 for (unsigned n = 1; n < halfSize; ++n) { 238 float omega = 2 * piFloat * n; 239 float invOmega = 1 / omega; 240 241 // Fourier coefficients according to standard definition. 242 float a; // Coefficient for cos(). 243 float b; // Coefficient for sin(). 244 245 // Calculate Fourier coefficients depending on the shape. 246 // Note that the overall scaling (magnitude) of the waveforms is normalized in createBandLimitedTables(). 247 switch (shape) { 248 case OscillatorNode::SINE: 249 // Standard sine wave function. 250 a = 0; 251 b = (n == 1) ? 1 : 0; 252 break; 253 case OscillatorNode::SQUARE: 254 // Square-shaped waveform with the first half its maximum value and the second half its minimum value. 255 a = 0; 256 b = invOmega * ((n & 1) ? 2 : 0); 257 break; 258 case OscillatorNode::SAWTOOTH: 259 // Sawtooth-shaped waveform with the first half ramping from zero to maximum and the second half from minimum to zero. 260 a = 0; 261 b = -invOmega * cos(0.5 * omega); 262 break; 263 case OscillatorNode::TRIANGLE: 264 // Triangle-shaped waveform going from its maximum value to its minimum value then back to the maximum value. 265 a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat); 266 b = 0; 267 break; 268 default: 269 ASSERT_NOT_REACHED(); 270 a = 0; 271 b = 0; 272 break; 273 } 274 275 realP[n] = a; 276 imagP[n] = b; 277 } 278 279 createBandLimitedTables(realP, imagP, halfSize); 280 } 281 282 } // namespace WebCore 283 284 #endif // ENABLE(WEB_AUDIO) 285