<!doctype html>
<html>
  <head>
    <title>Test ScriptProcessorNode</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>
      // Arbitrary sample rate
      const sampleRate = 48000;
      let audit = Audit.createTaskRunner();

      audit.define(
          {
            label: 'test',
            description: 'ScriptProcessor with stopped input source'
          },
          (task, should) => {
            // Two channels for testing.  Channel 0 is the output of the
            // scriptProcessor.  Channel 1 is the oscillator so we can compare
            // the outputs.
            let context = new OfflineAudioContext({
              numberOfChannels: 2,
              length: sampleRate,
              sampleRate: sampleRate
            });

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

            let src = new OscillatorNode(context);

            // Arbitrary buffer size for the ScriptProcessorNode.  Don't use 0;
            // we need to know the actual size to know the latency of the node
            // (easily).
            const spnSize = 512;
            let spn = context.createScriptProcessor(spnSize, 1, 1);

            // Arrange for the ScriptProcessor to add |offset| to the input.
            const offset = 1;
            spn.onaudioprocess = (event) => {
              let input = event.inputBuffer.getChannelData(0);
              let output = event.outputBuffer.getChannelData(0);
              for (let k = 0; k < output.length; ++k) {
                output[k] = input[k] + offset;
              }
            };

            src.connect(spn).connect(merger, 0, 0);
            src.connect(merger, 0, 1);

            // Start and stop the source.  The stop time is fairly arbitrary,
            // but use a render quantum boundary for simplicity.
            const stopFrame = RENDER_QUANTUM_FRAMES;
            src.start(0);
            src.stop(stopFrame / context.sampleRate);

            context.startRendering()
                .then(buffer => {
                  let ch0 = buffer.getChannelData(0);
                  let ch1 = buffer.getChannelData(1);

                  let shifted = ch1.slice(0, stopFrame).map(x => x + offset);

                  // SPN has a basic latency of 2*|spnSize| fraems, so the
                  // beginning is silent.
                  should(
                      ch0.slice(0, 2 * spnSize - 1),
                      `ScriptProcessor output[0:${2 * spnSize - 1}]`)
                      .beConstantValueOf(0);

                  // For the middle section (after adding latency), the output
                  // should be the source shifted by |offset|.
                  should(
                      ch0.slice(2 * spnSize, 2 * spnSize + stopFrame),
                      `ScriptProcessor output[${2 * spnSize}:${
                          2 * spnSize + stopFrame - 1}]`)
                      .beCloseToArray(shifted, {absoluteThreshold: 0});

                  // Output should be constant after the source has stopped.
                  // Include the latency introduced by the node.
                  should(
                      ch0.slice(2 * spnSize + stopFrame),
                      `ScriptProcessor output[${2 * spnSize + stopFrame}:]`)
                      .beConstantValueOf(offset);
                })
                .then(() => task.done());
          });

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