b=1012609 improve PeriodicWave phase-wrapping logic r=rillian
authorKarl Tomlinson <karlt+@karlt.net>
Tue, 30 Sep 2014 18:10:03 +1300
changeset 208297 fce56d505b2e116bb1e5bd453905a9c8fa92478d
parent 208296 a3e9214bfcce9698f99c08ac6795078759819b6e
child 208298 fd256e9a9dbccde2edd291e1bb0b4cf86114267f
push id27582
push userryanvm@gmail.com
push dateThu, 02 Oct 2014 16:20:06 +0000
treeherdermozilla-central@5d6ec4dddf14 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrillian
bugs1012609
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
b=1012609 improve PeriodicWave phase-wrapping logic r=rillian 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);
   }