Convert AudioParam NaN values to the default value

If any output value of an AudioParam (including the intrinsic values
and any inputs to the AudioParam), should be NaN, replace the NaN
value with the associated defaultValue.

This causes some slowdowns so SIMD/NEON code was added to mitigate the
degradation.  There is still some slowdown, but the worst case is now
about 7% slower on x86 and 10% on arm. Generally, the slowdown is less
than 2% and 5%, respectively.  (Perversely, some results got faster,
and the differences are statistically significant.)

Full details can be found at
https://docs.google.com/spreadsheets/d/1EhbLHm-9cUoEO5aj1vYemVBLQ3Dh4dCJPPLTfZPrZt4/edit?usp=sharing

Manually tested the test case from the bug and the issue no longer
occurs.

Bug: 1170531
Change-Id: I00d902b40a9ef9da990c6d68b664b1dcfc31b091
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2658724
Commit-Queue: Raymond Toy <rtoy@chromium.org>
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#851733}
diff --git a/webaudio/the-audio-api/the-audioparam-interface/nan-param.html b/webaudio/the-audio-api/the-audioparam-interface/nan-param.html
new file mode 100644
index 0000000..e9b8f0a
--- /dev/null
+++ b/webaudio/the-audio-api/the-audioparam-interface/nan-param.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Test Flushing of NaN to Zero in AudioParams</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+  </head>
+
+  <body>
+    <script>
+      let audit = Audit.createTaskRunner();
+
+      // See
+      // https://webaudio.github.io/web-audio-api/#computation-of-value.
+      //
+      // The computed value must replace NaN values in the output with
+      // the default value of the param.
+      audit.define('AudioParam NaN', async (task, should) => {
+        // For testing, we only need a small number of frames; and
+        // a low sample rate is perfectly fine.  Use two channels.
+        // The first channel is for the AudioParam output.  The
+        // second channel is for the AudioParam input.
+        let context = new OfflineAudioContext(
+            {numberOfChannels: 2, length: 256, sampleRate: 8192});
+        let merger = new ChannelMergerNode(
+            context, {numberOfInputs: context.destination.channelCount});
+        merger.connect(context.destination);
+
+        // A constant source with a huge value.
+        let mod = new ConstantSourceNode(context, {offset: 1e30});
+
+        // Gain nodes with a huge positive gain and huge negative
+        // gain.  Combined with the huge offset in |mod|, the
+        // output of the gain nodes are +Infinity and -Infinity.
+        let gainPos = new GainNode(context, {gain: 1e30});
+        let gainNeg = new GainNode(context, {gain: -1e30});
+
+        mod.connect(gainPos);
+        mod.connect(gainNeg);
+
+        // Connect these to the second merger channel. This is a
+        // sanity check that the AudioParam input really is NaN.
+        gainPos.connect(merger, 0, 1);
+        gainNeg.connect(merger, 0, 1);
+
+        // Source whose AudioParam is connected to the graph
+        // that produces NaN values.  Use a non-default value offset
+        // just in case something is wrong we get default for some
+        // other reason.
+        let src = new ConstantSourceNode(context, {offset: 100});
+
+        gainPos.connect(src.offset);
+        gainNeg.connect(src.offset);
+
+        // AudioParam output goes to channel 1 of the destination.
+        src.connect(merger, 0, 0);
+
+        // Let's go!
+        mod.start();
+        src.start();
+
+        let buffer = await context.startRendering();
+
+        let input = buffer.getChannelData(1);
+        let output = buffer.getChannelData(0);
+
+        // Have to test manually for NaN values in the input because
+        // NaN fails all comparisons.
+        let isNaN = true;
+        for (let k = 0; k < input.length; ++k) {
+          if (!Number.isNaN(input[k])) {
+            isNaN = false;
+            break;
+          }
+        }
+
+        should(isNaN, 'AudioParam input contains only NaN').beTrue();
+
+        // Output of the AudioParam should have all NaN values
+        // replaced by the default.
+        should(output, 'AudioParam output')
+            .beConstantValueOf(src.offset.defaultValue);
+
+        task.done();
+      });
+
+      audit.run();
+    </script>
+  </body>
+</html>