mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 865256 - Part 3d: Port blink's PeriodicWave to gecko. r=ehsan
Changes to use gecko infrastructure.
This commit is contained in:
parent
96f4f677b2
commit
260debb621
@ -26,65 +26,62 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if ENABLE(WEB_AUDIO)
|
||||
|
||||
#include "modules/webaudio/PeriodicWave.h"
|
||||
|
||||
#include "core/platform/audio/FFTFrame.h"
|
||||
#include "core/platform/audio/VectorMath.h"
|
||||
#include "modules/webaudio/OscillatorNode.h"
|
||||
#include "wtf/OwnPtr.h"
|
||||
#include "PeriodicWave.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "mozilla/FFTBlock.h"
|
||||
|
||||
const unsigned PeriodicWaveSize = 4096; // This must be a power of two.
|
||||
const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges.
|
||||
const float CentsPerRange = 1200 / 3; // 1/3 Octave.
|
||||
|
||||
using namespace mozilla;
|
||||
using mozilla::dom::OscillatorType;
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
using namespace VectorMath;
|
||||
|
||||
PassRefPtr<PeriodicWave> PeriodicWave::create(float sampleRate, Float32Array* real, Float32Array* imag)
|
||||
PeriodicWave* PeriodicWave::create(float sampleRate,
|
||||
const float* real,
|
||||
const float* imag,
|
||||
size_t numberOfComponents)
|
||||
{
|
||||
bool isGood = real && imag && real->length() == imag->length();
|
||||
ASSERT(isGood);
|
||||
bool isGood = real && imag && numberOfComponents > 0 &&
|
||||
numberOfComponents <= PeriodicWaveSize;
|
||||
MOZ_ASSERT(isGood);
|
||||
if (isGood) {
|
||||
RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate));
|
||||
size_t numberOfComponents = real->length();
|
||||
periodicWave->createBandLimitedTables(real->data(), imag->data(), numberOfComponents);
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->createBandLimitedTables(real, imag, numberOfComponents);
|
||||
return periodicWave;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PassRefPtr<PeriodicWave> PeriodicWave::createSine(float sampleRate)
|
||||
PeriodicWave* PeriodicWave::createSine(float sampleRate)
|
||||
{
|
||||
RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate));
|
||||
periodicWave->generateBasicWaveform(OscillatorNode::SINE);
|
||||
return periodicWave;
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->generateBasicWaveform(OscillatorType::Sine);
|
||||
return periodicWave;
|
||||
}
|
||||
|
||||
PassRefPtr<PeriodicWave> PeriodicWave::createSquare(float sampleRate)
|
||||
PeriodicWave* PeriodicWave::createSquare(float sampleRate)
|
||||
{
|
||||
RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate));
|
||||
periodicWave->generateBasicWaveform(OscillatorNode::SQUARE);
|
||||
return periodicWave;
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->generateBasicWaveform(OscillatorType::Square);
|
||||
return periodicWave;
|
||||
}
|
||||
|
||||
PassRefPtr<PeriodicWave> PeriodicWave::createSawtooth(float sampleRate)
|
||||
PeriodicWave* PeriodicWave::createSawtooth(float sampleRate)
|
||||
{
|
||||
RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate));
|
||||
periodicWave->generateBasicWaveform(OscillatorNode::SAWTOOTH);
|
||||
return periodicWave;
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
|
||||
return periodicWave;
|
||||
}
|
||||
|
||||
PassRefPtr<PeriodicWave> PeriodicWave::createTriangle(float sampleRate)
|
||||
PeriodicWave* PeriodicWave::createTriangle(float sampleRate)
|
||||
{
|
||||
RefPtr<PeriodicWave> periodicWave = adoptRef(new PeriodicWave(sampleRate));
|
||||
periodicWave->generateBasicWaveform(OscillatorNode::TRIANGLE);
|
||||
return periodicWave;
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->generateBasicWaveform(OscillatorType::Triangle);
|
||||
return periodicWave;
|
||||
}
|
||||
|
||||
PeriodicWave::PeriodicWave(float sampleRate)
|
||||
@ -93,7 +90,6 @@ PeriodicWave::PeriodicWave(float sampleRate)
|
||||
, m_numberOfRanges(NumberOfRanges)
|
||||
, m_centsPerRange(CentsPerRange)
|
||||
{
|
||||
ScriptWrappable::init(this);
|
||||
float nyquist = 0.5 * m_sampleRate;
|
||||
m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
|
||||
m_rateScale = m_periodicWaveSize / m_sampleRate;
|
||||
@ -101,27 +97,30 @@ PeriodicWave::PeriodicWave(float sampleRate)
|
||||
|
||||
void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
|
||||
{
|
||||
// Negative frequencies are allowed, in which case we alias to the positive frequency.
|
||||
// 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;
|
||||
float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
|
||||
|
||||
// Add one to round-up to the next range just in time to truncate partials before aliasing occurs.
|
||||
// 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.
|
||||
// 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();
|
||||
lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
|
||||
higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
|
||||
|
||||
// Ranges from 0 -> 1 to interpolate between lower -> higher.
|
||||
tableInterpolationFactor = pitchRange - rangeIndex1;
|
||||
@ -147,43 +146,47 @@ unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
|
||||
}
|
||||
|
||||
// Convert into time-domain wave buffers.
|
||||
// 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.
|
||||
// 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 PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
|
||||
{
|
||||
float normalizationScale = 1;
|
||||
|
||||
unsigned fftSize = m_periodicWaveSize;
|
||||
unsigned halfSize = fftSize / 2;
|
||||
unsigned halfSize = fftSize / 2 + 1;
|
||||
unsigned i;
|
||||
|
||||
numberOfComponents = std::min(numberOfComponents, halfSize);
|
||||
|
||||
m_bandLimitedTables.reserveCapacity(m_numberOfRanges);
|
||||
m_bandLimitedTables.SetCapacity(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();
|
||||
// This FFTBlock is used to cull partials (represented by frequency bins).
|
||||
FFTBlock frame(fftSize);
|
||||
float* realP = new float[halfSize];
|
||||
float* imagP = new float[halfSize];
|
||||
|
||||
// Copy from loaded frequency data and scale.
|
||||
float scale = fftSize;
|
||||
vsmul(realData, 1, &scale, realP, 1, numberOfComponents);
|
||||
vsmul(imagData, 1, &scale, imagP, 1, numberOfComponents);
|
||||
AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents);
|
||||
AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents);
|
||||
|
||||
// If fewer components were provided than 1/2 FFT size, then clear the remaining bins.
|
||||
// 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.
|
||||
// Generate complex conjugate because of the way the
|
||||
// inverse FFT is defined.
|
||||
float minusOne = -1;
|
||||
vsmul(imagP, 1, &minusOne, imagP, 1, halfSize);
|
||||
AudioBufferInPlaceScale(imagP, 1, minusOne, halfSize);
|
||||
|
||||
// Find the starting bin where we should start culling.
|
||||
// We need to clear out the highest frequencies to band-limit the waveform.
|
||||
// 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.
|
||||
@ -191,48 +194,56 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i
|
||||
realP[i] = 0;
|
||||
imagP[i] = 0;
|
||||
}
|
||||
// Clear packed-nyquist if necessary.
|
||||
// Clear nyquist if necessary.
|
||||
if (numberOfPartials < halfSize)
|
||||
imagP[0] = 0;
|
||||
realP[halfSize-1] = 0;
|
||||
|
||||
// Clear any DC-offset.
|
||||
realP[0] = 0;
|
||||
|
||||
// Clear values which have no effect.
|
||||
imagP[0] = 0;
|
||||
imagP[halfSize-1] = 0;
|
||||
|
||||
// Create the band-limited table.
|
||||
OwnPtr<AudioFloatArray> table = adoptPtr(new AudioFloatArray(m_periodicWaveSize));
|
||||
m_bandLimitedTables.append(table.release());
|
||||
AudioFloatArray* table = new AudioFloatArray(m_periodicWaveSize);
|
||||
m_bandLimitedTables.AppendElement(table);
|
||||
|
||||
// Apply an inverse FFT to generate the time-domain table data.
|
||||
float* data = m_bandLimitedTables[rangeIndex]->data();
|
||||
frame.doInverseFFT(data);
|
||||
float* data = m_bandLimitedTables[rangeIndex]->Elements();
|
||||
frame.PerformInverseFFT(realP, imagP, data);
|
||||
|
||||
// For the first range (which has the highest power), calculate its peak value then compute normalization scale.
|
||||
// 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_periodicWaveSize);
|
||||
maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
|
||||
|
||||
if (maxValue)
|
||||
normalizationScale = 1.0f / maxValue;
|
||||
}
|
||||
|
||||
// Apply normalization scale.
|
||||
vsmul(data, 1, &normalizationScale, data, 1, m_periodicWaveSize);
|
||||
AudioBufferInPlaceScale(data, 1, normalizationScale, m_periodicWaveSize);
|
||||
}
|
||||
}
|
||||
|
||||
void PeriodicWave::generateBasicWaveform(int shape)
|
||||
void PeriodicWave::generateBasicWaveform(OscillatorType shape)
|
||||
{
|
||||
const float piFloat = M_PI;
|
||||
unsigned fftSize = periodicWaveSize();
|
||||
unsigned halfSize = fftSize / 2;
|
||||
unsigned halfSize = fftSize / 2 + 1;
|
||||
|
||||
AudioFloatArray real(halfSize);
|
||||
AudioFloatArray imag(halfSize);
|
||||
float* realP = real.data();
|
||||
float* imagP = imag.data();
|
||||
float* realP = real.Elements();
|
||||
float* imagP = imag.Elements();
|
||||
|
||||
// Clear DC and Nyquist.
|
||||
realP[0] = 0;
|
||||
imagP[0] = 0;
|
||||
realP[halfSize-1] = 0;
|
||||
imagP[halfSize-1] = 0;
|
||||
|
||||
for (unsigned n = 1; n < halfSize; ++n) {
|
||||
float omega = 2 * piFloat * n;
|
||||
@ -243,30 +254,34 @@ void PeriodicWave::generateBasicWaveform(int shape)
|
||||
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().
|
||||
// Note that the overall scaling (magnitude) of the waveforms
|
||||
// is normalized in createBandLimitedTables().
|
||||
switch (shape) {
|
||||
case OscillatorNode::SINE:
|
||||
case OscillatorType::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.
|
||||
case OscillatorType::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.
|
||||
case OscillatorType::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.
|
||||
case OscillatorType::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();
|
||||
NS_NOTREACHED("invalid oscillator type");
|
||||
a = 0;
|
||||
b = 0;
|
||||
break;
|
||||
@ -280,5 +295,3 @@ void PeriodicWave::generateBasicWaveform(int shape)
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
#endif // ENABLE(WEB_AUDIO)
|
||||
|
@ -29,36 +29,40 @@
|
||||
#ifndef PeriodicWave_h
|
||||
#define PeriodicWave_h
|
||||
|
||||
#include "bindings/v8/ScriptWrappable.h"
|
||||
#include "core/platform/audio/AudioArray.h"
|
||||
#include "wtf/Float32Array.h"
|
||||
#include "wtf/OwnPtr.h"
|
||||
#include "wtf/PassRefPtr.h"
|
||||
#include "wtf/RefCounted.h"
|
||||
#include "wtf/RefPtr.h"
|
||||
#include "wtf/Vector.h"
|
||||
#include "mozilla/dom/OscillatorNodeBinding.h"
|
||||
#include <nsAutoPtr.h>
|
||||
#include <nsTArray.h>
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
class PeriodicWave : public ScriptWrappable, public RefCounted<PeriodicWave> {
|
||||
typedef nsTArray<float> AudioFloatArray;
|
||||
|
||||
class PeriodicWave {
|
||||
public:
|
||||
static PassRefPtr<PeriodicWave> createSine(float sampleRate);
|
||||
static PassRefPtr<PeriodicWave> createSquare(float sampleRate);
|
||||
static PassRefPtr<PeriodicWave> createSawtooth(float sampleRate);
|
||||
static PassRefPtr<PeriodicWave> createTriangle(float sampleRate);
|
||||
static PeriodicWave* createSine(float sampleRate);
|
||||
static PeriodicWave* createSquare(float sampleRate);
|
||||
static PeriodicWave* createSawtooth(float sampleRate);
|
||||
static PeriodicWave* createTriangle(float sampleRate);
|
||||
|
||||
// Creates an arbitrary periodic wave given the frequency components (Fourier coefficients).
|
||||
static PassRefPtr<PeriodicWave> create(float sampleRate, Float32Array* real, Float32Array* imag);
|
||||
// Creates an arbitrary periodic wave given the frequency components
|
||||
// (Fourier coefficients).
|
||||
static PeriodicWave* create(float sampleRate,
|
||||
const float* real,
|
||||
const float* imag,
|
||||
size_t numberOfComponents);
|
||||
|
||||
// Returns pointers to the lower and higher wave data for the pitch range containing
|
||||
// the given fundamental frequency. These two tables are in adjacent "pitch" ranges
|
||||
// where the higher table will have the maximum number of partials which won't alias when played back
|
||||
// at this fundamental frequency. The lower wave is the next range containing fewer partials than the higher wave.
|
||||
// Interpolation between these two tables can be made according to tableInterpolationFactor.
|
||||
// Where values from 0 -> 1 interpolate between lower -> higher.
|
||||
// Returns pointers to the lower and higher wave data for the pitch range
|
||||
// containing the given fundamental frequency. These two tables are in
|
||||
// adjacent "pitch" ranges where the higher table will have the maximum
|
||||
// number of partials which won't alias when played back at this
|
||||
// fundamental frequency. The lower wave is the next range containing fewer
|
||||
// partials than the higher wave. Interpolation between these two tables
|
||||
// can be made according to tableInterpolationFactor. Where values
|
||||
// from 0 -> 1 interpolate between lower -> higher.
|
||||
void waveDataForFundamentalFrequency(float, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor);
|
||||
|
||||
// Returns the scalar multiplier to the oscillator frequency to calculate wave buffer phase increment.
|
||||
// Returns the scalar multiplier to the oscillator frequency to calculate
|
||||
// wave buffer phase increment.
|
||||
float rateScale() const { return m_rateScale; }
|
||||
|
||||
unsigned periodicWaveSize() const { return m_periodicWaveSize; }
|
||||
@ -67,16 +71,17 @@ public:
|
||||
private:
|
||||
explicit PeriodicWave(float sampleRate);
|
||||
|
||||
void generateBasicWaveform(int);
|
||||
void generateBasicWaveform(mozilla::dom::OscillatorType);
|
||||
|
||||
float m_sampleRate;
|
||||
unsigned m_periodicWaveSize;
|
||||
unsigned m_numberOfRanges;
|
||||
float m_centsPerRange;
|
||||
|
||||
// The lowest frequency (in Hertz) where playback will include all of the partials.
|
||||
// Playing back lower than this frequency will gradually lose more high-frequency information.
|
||||
// This frequency is quite low (~10Hz @ 44.1KHz)
|
||||
// The lowest frequency (in Hertz) where playback will include all of the
|
||||
// partials. Playing back lower than this frequency will gradually lose
|
||||
// more high-frequency information.
|
||||
// This frequency is quite low (~10Hz @ // 44.1KHz)
|
||||
float m_lowestFundamentalFrequency;
|
||||
|
||||
float m_rateScale;
|
||||
@ -90,7 +95,7 @@ private:
|
||||
|
||||
// Creates tables based on numberOfComponents Fourier coefficients.
|
||||
void createBandLimitedTables(const float* real, const float* imag, unsigned numberOfComponents);
|
||||
Vector<OwnPtr<AudioFloatArray> > m_bandLimitedTables;
|
||||
nsTArray<nsAutoPtr<AudioFloatArray> > m_bandLimitedTables;
|
||||
};
|
||||
|
||||
} // namespace WebCore
|
||||
|
@ -17,6 +17,7 @@ CPP_SOURCES += [
|
||||
'HRTFElevation.cpp',
|
||||
'HRTFKernel.cpp',
|
||||
'HRTFPanner.cpp',
|
||||
'PeriodicWave.cpp',
|
||||
'Reverb.cpp',
|
||||
'ReverbAccumulationBuffer.cpp',
|
||||
'ReverbConvolver.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user