<!doctype html>
<html>
  <head>
    <title>Test k-rate AudioParam of ConstantSourceNode</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>
    <script src="automation-rate-testing.js"></script>
  </head>

  <body>
    <script>
      let audit = Audit.createTaskRunner();

      audit.define('ConstantSource k-rate offset', (task, should) => {
        // Arbitrary sample rate and duration.
        let sampleRate = 8000;

        // Only new a few render quanta to verify things are working.
        let testDuration = 4 * 128 / sampleRate;

        let context = new OfflineAudioContext({
          numberOfChannels: 3,
          sampleRate: sampleRate,
          length: testDuration * sampleRate
        });

        doTest(context, should, {
          sourceNodeName: 'none',
          verifyPieceWiseConstant: true,
          nodeName: 'ConstantSourceNode',
          prefix: 'k-rate offset',
          rateSettings: [{name: 'offset', value: 'k-rate'}],
          automations: [{
            name: 'offset',
            methods: [
              {name: 'setValueAtTime', options: [0, 0]}, {
                name: 'linearRampToValueAtTime',
                options: [10, testDuration]
              }
            ]
          }]
        }).then(() => task.done());
      });

      // Parameters for the For the following tests.

      // Must be power of two to eliminate round-off
      const sampleRate8k = 8192;

      // Arbitrary duration that doesn't need to be too long to verify k-rate
      // automations.  Probably should be at least a few render quanta.
      const testDuration = 8 * RENDER_QUANTUM_FRAMES / sampleRate8k;

      // Basic test that k-rate ConstantSourceNode.offset is k-rate.  This is
      // the basis for all of the following tests, so make sure it's right.
      audit.define(
          {
            label: 'ConstantSourceNode.offset k-rate automation',
            description:
                'Explicitly test ConstantSourceNode.offset k-rate automation is k-rate'
          },
          (task, should) => {
            let context = new OfflineAudioContext({
              numberOfChannels: 2,
              sampleRate: sampleRate8k,
              length: testDuration * sampleRate8k
            });
            let merger = new ChannelMergerNode(
                context, {numberOfInputs: context.destination.channelCount});
            merger.connect(context.destination);

            // k-rate ConstantSource.offset using a linear ramp starting at 0
            // and incrementing by 1 for each frame.
            let src = new ConstantSourceNode(context, {offset: 0});
            src.offset.automationRate = 'k-rate';

            src.offset.setValueAtTime(0, 0);
            src.offset.linearRampToValueAtTime(context.length, testDuration);

            src.connect(merger, 0, 0);

            src.start();

            // a-rate ConstantSource using the same ramp as above.
            let refSrc = new ConstantSourceNode(context, {offset: 0});

            refSrc.offset.setValueAtTime(0, 0);
            refSrc.offset.linearRampToValueAtTime(context.length, testDuration);

            refSrc.connect(merger, 0, 1);

            refSrc.start();

            context.startRendering()
                .then(buffer => {
                  let actual = buffer.getChannelData(0);
                  let expected = buffer.getChannelData(1);

                  for (let k = 0; k < actual.length;
                       k += RENDER_QUANTUM_FRAMES) {
                    // Verify that the k-rate output is constant over the render
                    // and that it matches the value of the a-rate value at the
                    // beginning of the render.
                    should(
                        actual.slice(k, k + RENDER_QUANTUM_FRAMES),
                        `k-rate ConstantSource.offset:  output[${k}:${
                            k + RENDER_QUANTUM_FRAMES}]`)
                        .beConstantValueOf(expected[k]);
                  }
                })
                .then(() => task.done());
          });

      // This test verifies that a k-rate input to the ConstantSourceNode.offset
      // works just as if we set the AudioParam to be k-rate.  This is the basis
      // of the following tests, so make sure it works.
      audit.define(
          {
            label: 'ConstantSource.offset',
            description: 'Verify k-rate automation matches k-rate input'
          },
          (task, should) => {
            let context = new OfflineAudioContext({
              numberOfChannels: 2,
              sampleRate: sampleRate8k,
              length: testDuration * sampleRate8k
            });

            let merger = new ChannelMergerNode(
                context, {numberOfInputs: context.destination.channelCount});
            merger.connect(context.destination);

            let tstSrc = new ConstantSourceNode(context);
            let tstMod = new ConstantSourceNode(context);
            tstSrc.offset.automationRate = 'k-rate';
            tstMod.offset.linearRampToValueAtTime(context.length, testDuration);

            tstMod.connect(tstSrc.offset)
            tstSrc.connect(merger, 0, 0);

            let refSrc = new ConstantSourceNode(context);
            let refMod = new ConstantSourceNode(context);
            refMod.offset.linearRampToValueAtTime(context.length, testDuration);
            refMod.offset.automationRate = 'k-rate';

            refMod.connect(refSrc.offset);
            refSrc.connect(merger, 0, 1);

            tstSrc.start();
            tstMod.start();
            refSrc.start();
            refMod.start();

            context.startRendering()
                .then(buffer => {
                  let actual = buffer.getChannelData(0);
                  let expected = buffer.getChannelData(1);

                  for (let k = 0; k < context.length;
                       k += RENDER_QUANTUM_FRAMES) {
                    should(
                        actual.slice(k, k + RENDER_QUANTUM_FRAMES),
                        `ConstantSource.offset k-rate input: output[${k}:${
                            k + RENDER_QUANTUM_FRAMES}]`)
                        .beConstantValueOf(expected[k]);
                  }
                })
                .then(() => task.done());
          });

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