Bug 1012609 - Improve PeriodicWave phase-wrapping logic. r=rillian, a=1.4+
authorKarl Tomlinson <karlt+@karlt.net>
Tue, 30 Sep 2014 18:10:03 +1300
changeset 188631 af991b3e4c9c4f30777351ae2ee2d2d05ff0a7ab
parent 188630 34a109087197609c5f72749e4428f2ddb31261a3
child 188632 a7f9278ad6ab794e7ed3a3509242df0e7cb52d93
push id720
push userryanvm@gmail.com
push dateFri, 03 Oct 2014 01:04:35 +0000
treeherdermozilla-b2g30_v1_4@ea144d59c01e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrillian, 1
bugs1012609
milestone30.0
Bug 1012609 - Improve PeriodicWave phase-wrapping logic. r=rillian, a=1.4+ 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.
content/media/webaudio/OscillatorNode.cpp
--- 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;
+      j1 &= indexMask;
       uint32_t j2 = j1 + 1;
-      if (j2 >= periodicWaveSize) {
-        j2 -= periodicWaveSize;
-      }
-      float sampleInterpolationFactor = mPhase - j1;
+      j2 &= indexMask;
+
+      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);
   }