Bug 1012609 - Improve PeriodicWave phase-wrapping logic. r=rillian, a=abillings
 author Karl Tomlinson Tue, 30 Sep 2014 18:10:03 +1300 changeset 200342 8c431dcec0ffde13988d47eacf341113ea883245 parent 200341 4e5404763aba54edf98ca0988c8cb2d1e8ad2818 child 200343 6023f0b4f8ba49dd117106cc98cd8007c2142bf6 push id 78 push user ryanvm@gmail.com push date Thu, 02 Oct 2014 13:18:20 +0000 treeherder mozilla-esr31@8c431dcec0ff [default view] [failures only] perfherder [talos] [build metrics] [platform microbench] (compared to previous push) reviewers rillian, abillings bugs 1012609 milestone 31.1.2
Bug 1012609 - Improve PeriodicWave phase-wrapping logic. r=rillian, a=abillings This approach takes advantage of the fact that periodicWaveSize is a power of 2 and uses unsigned integer modulo arithmetic to find the appropriate phase of the period.
```--- a/content/media/webaudio/OscillatorNode.cpp
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -368,46 +368,54 @@ public:
void ComputeCustom(float* aOutput,
TrackTicks ticks,
uint32_t aStart,
uint32_t aEnd)
{
MOZ_ASSERT(mPeriodicWave, "No custom waveform data");

uint32_t periodicWaveSize = mPeriodicWave->periodicWaveSize();
+    // Mask to wrap wave data indices into the range [0,periodicWaveSize).
+    uint32_t indexMask = periodicWaveSize - 1;
+    MOZ_ASSERT(periodicWaveSize && (periodicWaveSize & indexMask) == 0,
+               "periodicWaveSize must be power of 2");
float* higherWaveData = nullptr;
float* lowerWaveData = nullptr;
float tableInterpolationFactor;
// Phase increment at frequency of 1 Hz.
// mPhase runs [0,periodicWaveSize) here instead of [0,2*M_PI).
float basePhaseIncrement =
static_cast<float>(periodicWaveSize) / mSource->SampleRate();

for (uint32_t i = aStart; i < aEnd; ++i) {
UpdateParametersIfNeeded(ticks, i);
mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency,
lowerWaveData,
higherWaveData,
tableInterpolationFactor);
-      mPhase = fmod(mPhase, periodicWaveSize);
// Bilinear interpolation between adjacent samples in each table.
-      uint32_t j1 = floor(mPhase);
+      float floorPhase = floorf(mPhase);
+      uint32_t j1 = floorPhase;
uint32_t j2 = j1 + 1;
-      if (j2 >= periodicWaveSize) {
-        j2 -= periodicWaveSize;
-      }
-      float sampleInterpolationFactor = mPhase - j1;
+
+      float sampleInterpolationFactor = mPhase - floorPhase;
+
float lower = (1.0f - sampleInterpolationFactor) * lowerWaveData[j1] +
sampleInterpolationFactor * lowerWaveData[j2];
float higher = (1.0f - sampleInterpolationFactor) * higherWaveData[j1] +
sampleInterpolationFactor * higherWaveData[j2];
aOutput[i] = (1.0f - tableInterpolationFactor) * lower +
tableInterpolationFactor * higher;

-      mPhase += basePhaseIncrement * mFinalFrequency;
+      // Calculate next phase position from wrapped value j1 to avoid loss of
+      // precision at large values.
+      mPhase =
+        j1 + sampleInterpolationFactor + basePhaseIncrement * mFinalFrequency;
}
}

void ComputeSilence(AudioChunk *aOutput)
{
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
}
```