Bug 1265408 - Avoid subnormals in IIRFilter; r?karlt draft
authorDan Minor <dminor@mozilla.com>
Wed, 25 May 2016 09:11:21 -0400
changeset 372083 aab08a556fcb4ef7fc083d357fe8a55033d2d2c4
parent 372082 7baaf46f1f721c139aa9868b36b3b2c8cddee131
child 372084 7529f32d44ed10705ae4fb104d3d8af070cfafa0
push id19434
push userdminor@mozilla.com
push dateFri, 27 May 2016 11:00:28 +0000
reviewerskarlt
bugs1265408
milestone49.0a1
Bug 1265408 - Avoid subnormals in IIRFilter; r?karlt MozReview-Commit-ID: F4NUE8834tM
dom/media/webaudio/blink/Biquad.cpp
dom/media/webaudio/blink/IIRFilter.cpp
--- a/dom/media/webaudio/blink/Biquad.cpp
+++ b/dom/media/webaudio/blink/Biquad.cpp
@@ -71,16 +71,17 @@ void Biquad::process(const float* source
         x2 = x1;
         x1 = x;
         y2 = y1;
         y1 = y;
     }
 
     // Avoid introducing a stream of subnormals when input is silent and the
     // tail approaches zero.
+    // TODO: Remove this code when Bug 1157635 is fixed.
     if (x1 == 0.0 && x2 == 0.0 && (y1 != 0.0 || y2 != 0.0) &&
         fabs(y1) < FLT_MIN && fabs(y2) < FLT_MIN) {
       // Flush future values to zero (until there is new input).
       y1 = y2 = 0.0;
       // Flush calculated values.
       for (int i = framesToProcess; i-- && fabsf(destP[i]) < FLT_MIN; ) {
         destP[i] = 0.0f;
       }
--- a/dom/media/webaudio/blink/IIRFilter.cpp
+++ b/dom/media/webaudio/blink/IIRFilter.cpp
@@ -71,35 +71,58 @@ void IIRFilter::process(const float* sou
 
     int feedbackLength = m_feedback->Length();
     int feedforwardLength = m_feedforward->Length();
     int minLength = std::min(feedbackLength, feedforwardLength);
 
     double* xBuffer = m_xBuffer.Elements();
     double* yBuffer = m_yBuffer.Elements();
 
+    int inputZerosCount = 0;
+    int outputZeroOrSubnormalCount = 0;
+
     for (size_t n = 0; n < framesToProcess; ++n) {
         // To help minimize roundoff, we compute using double's, even though the filter coefficients
         // only have single precision values.
         double yn = feedforward[0] * sourceP[n];
 
+        // Keep track of how many zeros we have seen in a row in the input
+        if (yn == 0.0) {
+            inputZerosCount++;
+        } else {
+            inputZerosCount = 0;
+        }
+
         // Run both the feedforward and feedback terms together, when possible.
         for (int k = 1; k < minLength; ++k) {
             int n = (m_bufferIndex - k) & (kBufferLength - 1);
             yn += feedforward[k] * xBuffer[n];
             yn -= feedback[k] * yBuffer[n];
         }
 
         // Handle any remaining feedforward or feedback terms.
         for (int k = minLength; k < feedforwardLength; ++k)
             yn += feedforward[k] * xBuffer[(m_bufferIndex - k) & (kBufferLength - 1)];
 
         for (int k = minLength; k < feedbackLength; ++k)
             yn -= feedback[k] * yBuffer[(m_bufferIndex - k) & (kBufferLength - 1)];
 
+        // Keep track of the number of zeros or subnormals in the output
+        if (fabs(yn) < FLT_MIN) {
+            outputZeroOrSubnormalCount++;
+        } else {
+            outputZeroOrSubnormalCount = 0;
+        }
+
+        // Avoid introducing a stream of subnormals
+        // TODO: Remove this code when Bug 1157635 is fixed.
+        if (inputZerosCount >= feedforwardLength && outputZeroOrSubnormalCount >= feedbackLength) {
+            yn = 0.0;
+        }
+
         // Save the current input and output values in the memory buffers for the next output.
         m_xBuffer[m_bufferIndex] = sourceP[n];
         m_yBuffer[m_bufferIndex] = yn;
 
         m_bufferIndex = (m_bufferIndex + 1) & (kBufferLength - 1);
 
         // Avoid introducing a stream of subnormals
         // TODO: Remove this code when Bug 1157635 is fixed.