<!DOCTYPE html>
<html>
  <head>
    <title>
      Test Different PeriodicWave Lengths at Different Sample Rates
    </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>
  </head>
  <body>
    <script id="layout-test-code">
      // Test PeriodicWave objects with varying number of coefficients at
      // different sample rates. Basically, verify that the coefficients are
      // used at the appropriate sample rates.  This is done by comparing the
      // outputs of two periodic waves used in oscillators.  The output should
      // either be exactly zero or not depending on whether the coefficients
      // were used.

      let renderLength = 1;
      let context;

      let audit = Audit.createTaskRunner();

      // The set of Audit tests to be run to verify that PeriodicWave is using
      // the correct number of coefficients.  The name does not have to be
      // unique; the index of the entry is appended to the test.  Every entry
      // (except the last) needs sampleRate, bigWave, smallWave, and verifier
      // values.

      let testSet = [
        // Tests for contexts at 192 kHz.

        // Test that we use more than 2048 Fourier coefficients at 192 kHz
        // sample rate.  Basically verifies that 8192 is acceptable.
        {
          name: '192khz-test-1',
          sampleRate: 192000,
          bigWave: 8192,
          smallWave: 2048,
          verifier: resultShouldBeNonZero
        },

        // Test that we use at least 2049 Fourier coefficients at 192 kHz sample
        // rate.
        {
          name: '192khz-test-2',
          sampleRate: 192000,
          bigWave: 2049,
          smallWave: 2048,
          verifier: resultShouldBeNonZero
        },

        // Test that we use all 8192 Fourier coefficients at 192 kHz sample
        // rate.
        {
          name: '192khz-test-3',
          sampleRate: 192000,
          bigWave: 8192,
          smallWave: 8191,
          verifier: resultShouldBeNonZero
        },

        // Tests for contexts at 48 kHz.

        // Test that we do not use more than 2048 Fourier coefficients at 48
        // kHz.  This depends on the internal implementation where, for backward
        // compatibility and speed, we only use 2048 coefficients at 48 kHz.
        // (This is also true for rates below 88.2 kHz.)  Also tests that 8192
        // coefficients are allowed (but not all coefficients are used, of
        // course).
        {
          name: '48khz-test-1',
          sampleRate: 48000,
          bigWave: 8192,
          smallWave: 2048,
          verifier: resultShouldBeZero
        },

        // Test that we do not use more than 2048 Fourier coefficients.
        {
          name: '48khz-test-2',
          sampleRate: 48000,
          bigWave: 2049,
          smallWave: 2048,
          verifier: resultShouldBeZero
        },

        // It's not immediately clear with single-preicison arithmetic that we
        // can distinguish between 2049 and 2048 coefficients, so do one more
        // test with slightly more coefficients.
        {
          name: '48khz-test-3',
          sampleRate: 48000,
          bigWave: (2 + 1 / 64) * 1024,
          smallWave: 2048,
          verifier: resultShouldBeZero
        },

        // Test that we use at least 2048 Fourier coefficients at 48 kHz.
        // Ideally we want to compare 2047 and 2048 coefficients, but
        // single-precision arithmetic makes the resulting waveforms the same.
        // Hence use a smaller value that produces different waveforms.
        {
          name: '48khz-test-4',
          sampleRate: 48000,
          bigWave: 2048,
          smallWave: 2046,
          verifier: resultShouldBeNonZero
        },

        // Tests for contexts at 24 kHz.

        // Test that we do not use more than 1024 Fourier coefficients at 24
        // kHz.
        {
          name: '24khz-test-1',
          sampleRate: 24000,
          bigWave: 8192,
          smallWave: 1024,
          verifier: resultShouldBeZero
        },

        // Test that we do not use more than 1024 Fourier coefficients at 24
        // kHz.
        {
          name: '24khz-test-2',
          sampleRate: 24000,
          bigWave: 1025,
          smallWave: 1024,
          verifier: resultShouldBeZero
        },

        // Test that we use at least 1024 Fourier coefficients at 24 kHz.
        // Again, 1023 and 1024 produce the same waveforms in single-precisiion
        // so use a smaller wave table size.
        {
          name: '24khz-test-3',
          sampleRate: 24000,
          bigWave: 1024,
          smallWave: 1022,
          verifier: resultShouldBeNonZero
        },
      ];

      function generatePrefix(sampleRate, bigLength, smallLength) {
        return 'At ' + (sampleRate / 1000) + ' kHz, PeriodicWave with ' +
            bigLength + ' coefficients vs ' + smallLength + ': ';
      }

      // Returns a function the verifies that the result is zero.  The
      // parameters control what is printed in the messages.
      function resultShouldBeZero(should, sampleRate, bigLength, smallLength) {
        return function(buffer) {
          let prefix = generatePrefix(sampleRate, bigLength, smallLength);
          should(isBufferZero(buffer), prefix + 'are identical')
              .beEqualTo(true);
        }
      }

      // Returns a function the verifies that the result is non-zero.  The
      // parameters control what is printed in the messages.
      function resultShouldBeNonZero(
          should, sampleRate, bigLength, smallLength) {
        return function(buffer) {
          let prefix = generatePrefix(sampleRate, bigLength, smallLength);
          should(!isBufferZero(buffer), prefix + 'are different')
              .beEqualTo(true);
        }
      }

      // Creates a function that is used to run an Audit test for a given sample
      // rate, periodic wave sizes, and verifier.
      function createAuditTestFunction(
          sampleRate, bigLength, smallLength, verifier) {
        return (task, should) => {
          // Create the audio graph, render it, and then verify that the output
          // is the expected result.
          createAudioGraph(sampleRate, bigLength, smallLength);

          return context.startRendering()
              .then(verifier(should, sampleRate, bigLength, smallLength))
              .then(() => task.done());
        }
      }

      // Create the audio graph for the test.
      function createAudioGraph(
          sampleRate, bigPeriodicWaveLength, smallPeriodicWaveLength) {
        context =
            new OfflineAudioContext(1, renderLength * sampleRate, sampleRate);

        // Two PeriodicWave objects are created with different sizes (small and
        // big).  The contents are the same except that the samll sized
        // PeriodicWave has fewer coefficients.
        let smallWaveRealCoef = new Float32Array(smallPeriodicWaveLength);
        let smallWaveImagCoef = new Float32Array(smallPeriodicWaveLength);
        let bigWaveRealCoef = new Float32Array(bigPeriodicWaveLength);
        let bigWaveImagCoef = new Float32Array(bigPeriodicWaveLength);

        // Set up the Fourier coefficients for a sawtooth wave.  We use
        // sawtooth because all coefficients are non-zero
        bigWaveImagCoef[0] = 0;
        for (let k = 1; k < bigPeriodicWaveLength; k++) {
          let piFactor = 2 / (Math.PI * k);
          bigWaveImagCoef[k] = piFactor * ((k % 2 === 0) ? -1 : 1);
          if (k < smallPeriodicWaveLength)
            smallWaveImagCoef[k] = bigWaveImagCoef[k];
        }

        let smallPeriodicWave =
            context.createPeriodicWave(smallWaveRealCoef, smallWaveImagCoef);
        let bigPeriodicWave =
            context.createPeriodicWave(bigWaveRealCoef, bigWaveImagCoef);

        // Create oscillators using these PeriodicWave's.
        let smallOscillator = context.createOscillator();
        let bigOscillator = context.createOscillator();

        smallOscillator.setPeriodicWave(smallPeriodicWave);
        bigOscillator.setPeriodicWave(bigPeriodicWave);

        // Use a frequency of 1 Hz to make the distinction easier.  Can't tell
        // from this test, but if you plot the signals from these oscillators,
        // it's very clear that they are different.
        smallOscillator.frequency.value = 1;
        bigOscillator.frequency.value = 1;

        // The desired output is the difference between these oscillators.
        let gain = context.createGain();
        gain.gain.value = -1;
        smallOscillator.connect(gain);

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

        // Start the oscillators.
        smallOscillator.start();
        bigOscillator.start();
      }

      // Return true if the buffer is exactly zero.
      function isBufferZero(buffer) {
        if (buffer.getChannelData(0).find(function(x) {
              return x != 0;
            }))
          return false;
        return true;
      }

      // Ensure the actual Audit test name is unique by prepending an index to
      // the provided test name.
      function actualTestName(name, index) {
        return index + ':' + name;
      }

      // Define the tasks based on the entries in testSet.
      function defineAuditTests() {
        for (let k = 0; k < testSet.length; ++k) {
          let {name, sampleRate, bigWave, smallWave, verifier} = testSet[k];
          let actualName = actualTestName(name, k);
          audit.define(
              actualName,
              createAuditTestFunction(
                  sampleRate, bigWave, smallWave, verifier));
        }
      }

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