<!DOCTYPE html>
<html>
  <head>
    <title>
      Test Sampling for SetTargetAtTime
    </title>
    <script src="../../imported/w3c/web-platform-tests/resources/testharness.js"></script>
    <script src="../../resources/testharnessreport.js"></script>
    <script src="../resources/audit-util.js"></script>
    <script src="../resources/audit.js"></script>
    <script src="../resources/audioparam-testing.js"></script>
  </head>
  <body>
    <script id="layout-test-code">
      // Some slow sample rate, but otherwise arbitrary.
      let sampleRate = 12800;

      // Time constant for setTargetValue. Make it short, but is otherwise
      // arbitrary.
      let timeConstant = 10 / sampleRate;

      // Defaut initial gain for test.  Arbitrary, but we want it large so the
      // changes look large, even if the relative change isn't.
      let initialGain = 10000;

      let audit = Audit.createTaskRunner();

      // Test sampling of setTargetAtTime that starts at |startFrame|.  A gain
      // node is used for testing.  |initializeGainFunction| initializes the
      // gain value.
      function doTest(
          should, message, startFrame, threshold, initializeGainFunction) {
        let context = new OfflineAudioContext(1, 256, sampleRate);
        let source = context.createBufferSource();
        let b = context.createBuffer(1, 1, sampleRate);
        b.getChannelData(0)[0] = 1;
        source.buffer = b;
        source.loop = true;

        let gain = context.createGain();
        // Initialize the value of the gain node appropriately.
        initializeGainFunction(gain);
        gain.gain.setTargetAtTime(0, startFrame / sampleRate, timeConstant);

        source.connect(gain);
        gain.connect(context.destination);

        source.start();

        return context.startRendering().then(function(resultBuffer) {
          // Verify that the sampling of the setTargetAtTime automation was done
          // correctly. We just look at the sample just past the start of the
          // automation.
          let resultData = resultBuffer.getChannelData(0);
          // Compute the true result at the frame just past startFrame and
          // verify that the actual rendered result is within |threshold| of the
          // expected value.
          let frame = Math.ceil(startFrame);
          let v = 10000 *
              Math.exp(
                  -(frame / sampleRate - startFrame / sampleRate) /
                  timeConstant);
          should(
              resultData[frame], message + ': Target value at frame ' + frame)
              .beCloseTo(v, {threshold: threshold});
        });
      }

      function initializeGainBySetter(g) {
        g.gain.value = initialGain;
      }

      function initializeGainBySetValue(g) {
        g.gain.setValueAtTime(initialGain, 0);
      }

      audit.define('setValue;128.1', (task, should) => {
        doTest(
            should, 'Initialize by setValueAtTime', 128.1, 3.6029e-8,
            initializeGainBySetValue)
            .then(() => task.done());
      });

      audit.define('setValue;0.1', (task, should) => {
        doTest(
            should, 'Initialize by setValueAtTime', 0.1, 3.6029e-8,
            initializeGainBySetValue)
            .then(() => task.done());
      });

      audit.define('setValue;0.0', (task, should) => {
        doTest(
            should, 'Initialize by setValueAtTime', 0, 3.6029e-8,
            initializeGainBySetValue)
            .then(() => task.done());
      });

      audit.define('setter;128.1', (task, should) => {
        doTest(
            should, 'Initialize by setter', 128.1, 3.6029e-8,
            initializeGainBySetter)
            .then(() => task.done());
      });

      audit.define('setter;0.1', (task, should) => {
        doTest(
            should, 'Initialize by setter', 0.1, 3.6029e-8,
            initializeGainBySetter)
            .then(() => task.done());
      });

      audit.define('setter;0.0', (task, should) => {
        doTest(should, 'Initialize by setter', 0, 0, initializeGainBySetter)
            .then(() => task.done());
      });

      audit.run();
    </script>
  </body>
</html>
