<!DOCTYPE html>
<!--
Tests that multiple audio-rate signals (AudioNode outputs) can be connected to an AudioParam
and that these signals are summed, along with the AudioParams intrinsic value.
-->
<html>
  <head>
    <title>
      audioparam-summingjunction.html
    </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="/webaudio/resources/mix-testing.js"></script>
  </head>
  <body>
    <script id="layout-test-code">
      let audit = Audit.createTaskRunner();

      let sampleRate = 44100.0;
      let lengthInSeconds = 1;

      let context = 0;

      // Buffers used by the two gain controlling sources.
      let linearRampBuffer;
      let toneBuffer;
      let toneFrequency = 440;

      // Arbitrary non-zero value.
      let baselineGain = 5;

      // Allow for a small round-off error.
      let maxAllowedError = 1e-6;

      function checkResult(renderedBuffer, should) {
        let renderedData = renderedBuffer.getChannelData(0);

        // Get buffer data from the two sources used to control gain.
        let linearRampData = linearRampBuffer.getChannelData(0);
        let toneData = toneBuffer.getChannelData(0);

        let n = renderedBuffer.length;

        should(n, 'Rendered signal length').beEqualTo(linearRampBuffer.length);

        // Check that the rendered result exactly matches the sum of the
        // intrinsic gain plus the two sources used to control gain. This is
        // because we're changing the gain of a signal having constant value 1.
        let success = true;
        for (let i = 0; i < n; ++i) {
          let expectedValue = baselineGain + linearRampData[i] + toneData[i];
          let error = Math.abs(expectedValue - renderedData[i]);

          if (error > maxAllowedError) {
            success = false;
            break;
          }
        }

        should(
            success,
            'Rendered signal matches sum of two audio-rate gain changing signals plus baseline gain')
            .beTrue();
      }

      audit.define('test', function(task, should) {
        let sampleFrameLength = sampleRate * lengthInSeconds;

        // Create offline audio context.
        context = new OfflineAudioContext(1, sampleFrameLength, sampleRate);

        // Create buffer used by the source which will have its gain controlled.
        let constantOneBuffer =
            createConstantBuffer(context, sampleFrameLength, 1);
        let constantSource = context.createBufferSource();
        constantSource.buffer = constantOneBuffer;

        // Create 1st buffer used to control gain (a linear ramp).
        linearRampBuffer = createLinearRampBuffer(context, sampleFrameLength);
        let gainSource1 = context.createBufferSource();
        gainSource1.buffer = linearRampBuffer;

        // Create 2st buffer used to control gain (a simple sine wave tone).
        toneBuffer =
            createToneBuffer(context, toneFrequency, lengthInSeconds, 1);
        let gainSource2 = context.createBufferSource();
        gainSource2.buffer = toneBuffer;

        // Create a gain node controlling the gain of constantSource and make
        // the connections.
        let gainNode = context.createGain();

        // Intrinsic baseline gain.
        // This gain value should be summed with gainSource1 and gainSource2.
        gainNode.gain.value = baselineGain;

        constantSource.connect(gainNode);
        gainNode.connect(context.destination);

        // Connect two audio-rate signals to control the .gain AudioParam.
        gainSource1.connect(gainNode.gain);
        gainSource2.connect(gainNode.gain);

        // Start all sources at time 0.
        constantSource.start(0);
        gainSource1.start(0);
        gainSource2.start(0);

        context.startRendering().then(buffer => {
          checkResult(buffer, should);
          task.done();
        });
      });

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