1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/blink/PeriodicWave.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,311 @@ 1.4 +/* 1.5 + * Copyright (C) 2012 Google Inc. All rights reserved. 1.6 + * 1.7 + * Redistribution and use in source and binary forms, with or without 1.8 + * modification, are permitted provided that the following conditions 1.9 + * are met: 1.10 + * 1.11 + * 1. Redistributions of source code must retain the above copyright 1.12 + * notice, this list of conditions and the following disclaimer. 1.13 + * 2. Redistributions in binary form must reproduce the above copyright 1.14 + * notice, this list of conditions and the following disclaimer in the 1.15 + * documentation and/or other materials provided with the distribution. 1.16 + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 1.17 + * its contributors may be used to endorse or promote products derived 1.18 + * from this software without specific prior written permission. 1.19 + * 1.20 + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 1.21 + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1.22 + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1.23 + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 1.24 + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1.25 + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 1.26 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1.27 + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.28 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 1.29 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.30 + */ 1.31 + 1.32 +#include "PeriodicWave.h" 1.33 +#include <algorithm> 1.34 +#include <cmath> 1.35 +#include "mozilla/FFTBlock.h" 1.36 + 1.37 +const unsigned PeriodicWaveSize = 4096; // This must be a power of two. 1.38 +const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges. 1.39 +const float CentsPerRange = 1200 / 3; // 1/3 Octave. 1.40 + 1.41 +using namespace mozilla; 1.42 +using mozilla::dom::OscillatorType; 1.43 + 1.44 +namespace WebCore { 1.45 + 1.46 +PeriodicWave* PeriodicWave::create(float sampleRate, 1.47 + const float* real, 1.48 + const float* imag, 1.49 + size_t numberOfComponents) 1.50 +{ 1.51 + bool isGood = real && imag && numberOfComponents > 0 && 1.52 + numberOfComponents <= PeriodicWaveSize; 1.53 + MOZ_ASSERT(isGood); 1.54 + if (isGood) { 1.55 + PeriodicWave* periodicWave = new PeriodicWave(sampleRate); 1.56 + periodicWave->createBandLimitedTables(real, imag, numberOfComponents); 1.57 + return periodicWave; 1.58 + } 1.59 + return 0; 1.60 +} 1.61 + 1.62 +PeriodicWave* PeriodicWave::createSine(float sampleRate) 1.63 +{ 1.64 + PeriodicWave* periodicWave = new PeriodicWave(sampleRate); 1.65 + periodicWave->generateBasicWaveform(OscillatorType::Sine); 1.66 + return periodicWave; 1.67 +} 1.68 + 1.69 +PeriodicWave* PeriodicWave::createSquare(float sampleRate) 1.70 +{ 1.71 + PeriodicWave* periodicWave = new PeriodicWave(sampleRate); 1.72 + periodicWave->generateBasicWaveform(OscillatorType::Square); 1.73 + return periodicWave; 1.74 +} 1.75 + 1.76 +PeriodicWave* PeriodicWave::createSawtooth(float sampleRate) 1.77 +{ 1.78 + PeriodicWave* periodicWave = new PeriodicWave(sampleRate); 1.79 + periodicWave->generateBasicWaveform(OscillatorType::Sawtooth); 1.80 + return periodicWave; 1.81 +} 1.82 + 1.83 +PeriodicWave* PeriodicWave::createTriangle(float sampleRate) 1.84 +{ 1.85 + PeriodicWave* periodicWave = new PeriodicWave(sampleRate); 1.86 + periodicWave->generateBasicWaveform(OscillatorType::Triangle); 1.87 + return periodicWave; 1.88 +} 1.89 + 1.90 +PeriodicWave::PeriodicWave(float sampleRate) 1.91 + : m_sampleRate(sampleRate) 1.92 + , m_periodicWaveSize(PeriodicWaveSize) 1.93 + , m_numberOfRanges(NumberOfRanges) 1.94 + , m_centsPerRange(CentsPerRange) 1.95 +{ 1.96 + float nyquist = 0.5 * m_sampleRate; 1.97 + m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials(); 1.98 + m_rateScale = m_periodicWaveSize / m_sampleRate; 1.99 +} 1.100 + 1.101 +size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const 1.102 +{ 1.103 + size_t amount = aMallocSizeOf(this); 1.104 + 1.105 + amount += m_bandLimitedTables.SizeOfExcludingThis(aMallocSizeOf); 1.106 + for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) { 1.107 + if (m_bandLimitedTables[i]) { 1.108 + amount += m_bandLimitedTables[i]->SizeOfIncludingThis(aMallocSizeOf); 1.109 + } 1.110 + } 1.111 + 1.112 + return amount; 1.113 +} 1.114 + 1.115 +void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor) 1.116 +{ 1.117 + // Negative frequencies are allowed, in which case we alias 1.118 + // to the positive frequency. 1.119 + fundamentalFrequency = fabsf(fundamentalFrequency); 1.120 + 1.121 + // Calculate the pitch range. 1.122 + float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5; 1.123 + float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200; 1.124 + 1.125 + // Add one to round-up to the next range just in time to truncate 1.126 + // partials before aliasing occurs. 1.127 + float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange; 1.128 + 1.129 + pitchRange = std::max(pitchRange, 0.0f); 1.130 + pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1)); 1.131 + 1.132 + // The words "lower" and "higher" refer to the table data having 1.133 + // the lower and higher numbers of partials. It's a little confusing 1.134 + // since the range index gets larger the more partials we cull out. 1.135 + // So the lower table data will have a larger range index. 1.136 + unsigned rangeIndex1 = static_cast<unsigned>(pitchRange); 1.137 + unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1; 1.138 + 1.139 + lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements(); 1.140 + higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements(); 1.141 + 1.142 + // Ranges from 0 -> 1 to interpolate between lower -> higher. 1.143 + tableInterpolationFactor = pitchRange - rangeIndex1; 1.144 +} 1.145 + 1.146 +unsigned PeriodicWave::maxNumberOfPartials() const 1.147 +{ 1.148 + return m_periodicWaveSize / 2; 1.149 +} 1.150 + 1.151 +unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const 1.152 +{ 1.153 + // Number of cents below nyquist where we cull partials. 1.154 + float centsToCull = rangeIndex * m_centsPerRange; 1.155 + 1.156 + // A value from 0 -> 1 representing what fraction of the partials to keep. 1.157 + float cullingScale = pow(2, -centsToCull / 1200); 1.158 + 1.159 + // The very top range will have all the partials culled. 1.160 + unsigned numberOfPartials = cullingScale * maxNumberOfPartials(); 1.161 + 1.162 + return numberOfPartials; 1.163 +} 1.164 + 1.165 +// Convert into time-domain wave buffers. 1.166 +// One table is created for each range for non-aliasing playback 1.167 +// at different playback rates. Thus, higher ranges have more 1.168 +// high-frequency partials culled out. 1.169 +void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents) 1.170 +{ 1.171 + float normalizationScale = 1; 1.172 + 1.173 + unsigned fftSize = m_periodicWaveSize; 1.174 + unsigned halfSize = fftSize / 2 + 1; 1.175 + unsigned i; 1.176 + 1.177 + numberOfComponents = std::min(numberOfComponents, halfSize); 1.178 + 1.179 + m_bandLimitedTables.SetCapacity(m_numberOfRanges); 1.180 + 1.181 + for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { 1.182 + // This FFTBlock is used to cull partials (represented by frequency bins). 1.183 + FFTBlock frame(fftSize); 1.184 + nsAutoArrayPtr<float> realP(new float[halfSize]); 1.185 + nsAutoArrayPtr<float> imagP(new float[halfSize]); 1.186 + 1.187 + // Copy from loaded frequency data and scale. 1.188 + float scale = fftSize; 1.189 + AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents); 1.190 + AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents); 1.191 + 1.192 + // If fewer components were provided than 1/2 FFT size, 1.193 + // then clear the remaining bins. 1.194 + for (i = numberOfComponents; i < halfSize; ++i) { 1.195 + realP[i] = 0; 1.196 + imagP[i] = 0; 1.197 + } 1.198 + 1.199 + // Generate complex conjugate because of the way the 1.200 + // inverse FFT is defined. 1.201 + float minusOne = -1; 1.202 + AudioBufferInPlaceScale(imagP, minusOne, halfSize); 1.203 + 1.204 + // Find the starting bin where we should start culling. 1.205 + // We need to clear out the highest frequencies to band-limit 1.206 + // the waveform. 1.207 + unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); 1.208 + 1.209 + // Cull the aliasing partials for this pitch range. 1.210 + for (i = numberOfPartials + 1; i < halfSize; ++i) { 1.211 + realP[i] = 0; 1.212 + imagP[i] = 0; 1.213 + } 1.214 + // Clear nyquist if necessary. 1.215 + if (numberOfPartials < halfSize) 1.216 + realP[halfSize-1] = 0; 1.217 + 1.218 + // Clear any DC-offset. 1.219 + realP[0] = 0; 1.220 + 1.221 + // Clear values which have no effect. 1.222 + imagP[0] = 0; 1.223 + imagP[halfSize-1] = 0; 1.224 + 1.225 + // Create the band-limited table. 1.226 + AudioFloatArray* table = new AudioFloatArray(m_periodicWaveSize); 1.227 + m_bandLimitedTables.AppendElement(table); 1.228 + 1.229 + // Apply an inverse FFT to generate the time-domain table data. 1.230 + float* data = m_bandLimitedTables[rangeIndex]->Elements(); 1.231 + frame.PerformInverseFFT(realP, imagP, data); 1.232 + 1.233 + // For the first range (which has the highest power), calculate 1.234 + // its peak value then compute normalization scale. 1.235 + if (!rangeIndex) { 1.236 + float maxValue; 1.237 + maxValue = AudioBufferPeakValue(data, m_periodicWaveSize); 1.238 + 1.239 + if (maxValue) 1.240 + normalizationScale = 1.0f / maxValue; 1.241 + } 1.242 + 1.243 + // Apply normalization scale. 1.244 + AudioBufferInPlaceScale(data, normalizationScale, m_periodicWaveSize); 1.245 + } 1.246 +} 1.247 + 1.248 +void PeriodicWave::generateBasicWaveform(OscillatorType shape) 1.249 +{ 1.250 + const float piFloat = M_PI; 1.251 + unsigned fftSize = periodicWaveSize(); 1.252 + unsigned halfSize = fftSize / 2 + 1; 1.253 + 1.254 + AudioFloatArray real(halfSize); 1.255 + AudioFloatArray imag(halfSize); 1.256 + float* realP = real.Elements(); 1.257 + float* imagP = imag.Elements(); 1.258 + 1.259 + // Clear DC and Nyquist. 1.260 + realP[0] = 0; 1.261 + imagP[0] = 0; 1.262 + realP[halfSize-1] = 0; 1.263 + imagP[halfSize-1] = 0; 1.264 + 1.265 + for (unsigned n = 1; n < halfSize; ++n) { 1.266 + float omega = 2 * piFloat * n; 1.267 + float invOmega = 1 / omega; 1.268 + 1.269 + // Fourier coefficients according to standard definition. 1.270 + float a; // Coefficient for cos(). 1.271 + float b; // Coefficient for sin(). 1.272 + 1.273 + // Calculate Fourier coefficients depending on the shape. 1.274 + // Note that the overall scaling (magnitude) of the waveforms 1.275 + // is normalized in createBandLimitedTables(). 1.276 + switch (shape) { 1.277 + case OscillatorType::Sine: 1.278 + // Standard sine wave function. 1.279 + a = 0; 1.280 + b = (n == 1) ? 1 : 0; 1.281 + break; 1.282 + case OscillatorType::Square: 1.283 + // Square-shaped waveform with the first half its maximum value 1.284 + // and the second half its minimum value. 1.285 + a = 0; 1.286 + b = invOmega * ((n & 1) ? 2 : 0); 1.287 + break; 1.288 + case OscillatorType::Sawtooth: 1.289 + // Sawtooth-shaped waveform with the first half ramping from 1.290 + // zero to maximum and the second half from minimum to zero. 1.291 + a = 0; 1.292 + b = -invOmega * cos(0.5 * omega); 1.293 + break; 1.294 + case OscillatorType::Triangle: 1.295 + // Triangle-shaped waveform going from its maximum value to 1.296 + // its minimum value then back to the maximum value. 1.297 + a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat); 1.298 + b = 0; 1.299 + break; 1.300 + default: 1.301 + NS_NOTREACHED("invalid oscillator type"); 1.302 + a = 0; 1.303 + b = 0; 1.304 + break; 1.305 + } 1.306 + 1.307 + realP[n] = a; 1.308 + imagP[n] = b; 1.309 + } 1.310 + 1.311 + createBandLimitedTables(realP, imagP, halfSize); 1.312 +} 1.313 + 1.314 +} // namespace WebCore