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