123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- /*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "config.h"
- #if ENABLE(WEB_AUDIO)
- #include "WaveTable.h"
- #include "FFTFrame.h"
- #include "OscillatorNode.h"
- #include "VectorMath.h"
- #include <algorithm>
- #include <wtf/OwnPtr.h>
- const unsigned WaveTableSize = 4096; // This must be a power of two.
- const unsigned NumberOfRanges = 36; // There should be 3 * log2(WaveTableSize) 1/3 octave ranges.
- const float CentsPerRange = 1200 / 3; // 1/3 Octave.
- namespace WebCore {
-
- using namespace VectorMath;
- PassRefPtr<WaveTable> WaveTable::create(float sampleRate, Float32Array* real, Float32Array* imag)
- {
- bool isGood = real && imag && real->length() == imag->length();
- ASSERT(isGood);
- if (isGood) {
- RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
- size_t numberOfComponents = real->length();
- waveTable->createBandLimitedTables(real->data(), imag->data(), numberOfComponents);
- return waveTable;
- }
- return 0;
- }
- PassRefPtr<WaveTable> WaveTable::createSine(float sampleRate)
- {
- RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
- waveTable->generateBasicWaveform(OscillatorNode::SINE);
- return waveTable;
- }
- PassRefPtr<WaveTable> WaveTable::createSquare(float sampleRate)
- {
- RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
- waveTable->generateBasicWaveform(OscillatorNode::SQUARE);
- return waveTable;
- }
- PassRefPtr<WaveTable> WaveTable::createSawtooth(float sampleRate)
- {
- RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
- waveTable->generateBasicWaveform(OscillatorNode::SAWTOOTH);
- return waveTable;
- }
- PassRefPtr<WaveTable> WaveTable::createTriangle(float sampleRate)
- {
- RefPtr<WaveTable> waveTable = adoptRef(new WaveTable(sampleRate));
- waveTable->generateBasicWaveform(OscillatorNode::TRIANGLE);
- return waveTable;
- }
- WaveTable::WaveTable(float sampleRate)
- : m_sampleRate(sampleRate)
- , m_waveTableSize(WaveTableSize)
- , m_numberOfRanges(NumberOfRanges)
- , m_centsPerRange(CentsPerRange)
- {
- float nyquist = 0.5 * m_sampleRate;
- m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
- m_rateScale = m_waveTableSize / m_sampleRate;
- }
- void WaveTable::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
- {
- // Negative frequencies are allowed, in which case we alias to the positive frequency.
- fundamentalFrequency = fabsf(fundamentalFrequency);
- // Calculate the pitch range.
- float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
- float centsAboveLowestFrequency = log2f(ratio) * 1200;
- // Add one to round-up to the next range just in time to truncate partials before aliasing occurs.
- float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
- pitchRange = std::max(pitchRange, 0.0f);
- pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1));
- // The words "lower" and "higher" refer to the table data having the lower and higher numbers of partials.
- // It's a little confusing since the range index gets larger the more partials we cull out.
- // So the lower table data will have a larger range index.
- unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
- unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
- lowerWaveData = m_bandLimitedTables[rangeIndex2]->data();
- higherWaveData = m_bandLimitedTables[rangeIndex1]->data();
-
- // Ranges from 0 -> 1 to interpolate between lower -> higher.
- tableInterpolationFactor = pitchRange - rangeIndex1;
- }
- unsigned WaveTable::maxNumberOfPartials() const
- {
- return m_waveTableSize / 2;
- }
- unsigned WaveTable::numberOfPartialsForRange(unsigned rangeIndex) const
- {
- // Number of cents below nyquist where we cull partials.
- float centsToCull = rangeIndex * m_centsPerRange;
- // A value from 0 -> 1 representing what fraction of the partials to keep.
- float cullingScale = pow(2, -centsToCull / 1200);
- // The very top range will have all the partials culled.
- unsigned numberOfPartials = cullingScale * maxNumberOfPartials();
- return numberOfPartials;
- }
- // Convert into time-domain wave tables.
- // One table is created for each range for non-aliasing playback at different playback rates.
- // Thus, higher ranges have more high-frequency partials culled out.
- void WaveTable::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
- {
- float normalizationScale = 1;
- unsigned fftSize = m_waveTableSize;
- unsigned halfSize = fftSize / 2;
- unsigned i;
-
- numberOfComponents = std::min(numberOfComponents, halfSize);
- m_bandLimitedTables.reserveCapacity(m_numberOfRanges);
- for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
- // This FFTFrame is used to cull partials (represented by frequency bins).
- FFTFrame frame(fftSize);
- float* realP = frame.realData();
- float* imagP = frame.imagData();
- // Copy from loaded frequency data and scale.
- float scale = fftSize;
- vsmul(realData, 1, &scale, realP, 1, numberOfComponents);
- vsmul(imagData, 1, &scale, imagP, 1, numberOfComponents);
- // If fewer components were provided than 1/2 FFT size, then clear the remaining bins.
- for (i = numberOfComponents; i < halfSize; ++i) {
- realP[i] = 0;
- imagP[i] = 0;
- }
-
- // Generate complex conjugate because of the way the inverse FFT is defined.
- float minusOne = -1;
- vsmul(imagP, 1, &minusOne, imagP, 1, halfSize);
- // Find the starting bin where we should start culling.
- // We need to clear out the highest frequencies to band-limit the waveform.
- unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
- // Cull the aliasing partials for this pitch range.
- for (i = numberOfPartials + 1; i < halfSize; ++i) {
- realP[i] = 0;
- imagP[i] = 0;
- }
- // Clear packed-nyquist if necessary.
- if (numberOfPartials < halfSize)
- imagP[0] = 0;
- // Clear any DC-offset.
- realP[0] = 0;
- // Create the band-limited table.
- OwnPtr<AudioFloatArray> table = adoptPtr(new AudioFloatArray(m_waveTableSize));
- m_bandLimitedTables.append(table.release());
- // Apply an inverse FFT to generate the time-domain table data.
- float* data = m_bandLimitedTables[rangeIndex]->data();
- frame.doInverseFFT(data);
- // For the first range (which has the highest power), calculate its peak value then compute normalization scale.
- if (!rangeIndex) {
- float maxValue;
- vmaxmgv(data, 1, &maxValue, m_waveTableSize);
- if (maxValue)
- normalizationScale = 1.0f / maxValue;
- }
- // Apply normalization scale.
- vsmul(data, 1, &normalizationScale, data, 1, m_waveTableSize);
- }
- }
- void WaveTable::generateBasicWaveform(int shape)
- {
- unsigned fftSize = waveTableSize();
- unsigned halfSize = fftSize / 2;
- AudioFloatArray real(halfSize);
- AudioFloatArray imag(halfSize);
- float* realP = real.data();
- float* imagP = imag.data();
- // Clear DC and Nyquist.
- realP[0] = 0;
- imagP[0] = 0;
- for (unsigned n = 1; n < halfSize; ++n) {
- float omega = 2 * piFloat * n;
- float invOmega = 1 / omega;
- // Fourier coefficients according to standard definition.
- float a; // Coefficient for cos().
- float b; // Coefficient for sin().
- // Calculate Fourier coefficients depending on the shape.
- // Note that the overall scaling (magnitude) of the waveforms is normalized in createBandLimitedTables().
- switch (shape) {
- case OscillatorNode::SINE:
- // Standard sine wave function.
- a = 0;
- b = (n == 1) ? 1 : 0;
- break;
- case OscillatorNode::SQUARE:
- // Square-shaped waveform with the first half its maximum value and the second half its minimum value.
- a = 0;
- b = invOmega * ((n & 1) ? 2 : 0);
- break;
- case OscillatorNode::SAWTOOTH:
- // Sawtooth-shaped waveform with the first half ramping from zero to maximum and the second half from minimum to zero.
- a = 0;
- b = -invOmega * cos(0.5 * omega);
- break;
- case OscillatorNode::TRIANGLE:
- // Triangle-shaped waveform going from its maximum value to its minimum value then back to the maximum value.
- a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat);
- b = 0;
- break;
- default:
- ASSERT_NOT_REACHED();
- a = 0;
- b = 0;
- break;
- }
- realP[n] = a;
- imagP[n] = b;
- }
- createBandLimitedTables(realP, imagP, halfSize);
- }
- } // namespace WebCore
- #endif // ENABLE(WEB_AUDIO)
|