<!DOCTYPE html>
<html>
  <head>
    <title>
      audiobuffersource-playbackrate-zero.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>
  </head>
  <body>
    <script id="layout-test-code">
      // Sample rate should be power of 128 to observe the change of AudioParam
      // at the beginning of rendering quantum. (playbackRate is k-rate) This is
      // the minimum sample rate in the valid sample rate range.
      let sampleRate = 8192;

      // The render duration in seconds, and the length in samples.
      let renderDuration = 1.0;
      let renderLength = renderDuration * sampleRate;

      let context = new OfflineAudioContext(1, renderLength, sampleRate);
      let audit = Audit.createTaskRunner();


      // Task: Render the actual buffer and compare with the reference.
      audit.define('synthesize-verify', (task, should) => {
        let ramp = context.createBufferSource();
        let rampBuffer = createLinearRampBuffer(context, renderLength);
        ramp.buffer = rampBuffer;

        ramp.connect(context.destination);
        ramp.start();

        // Leave the playbackRate as 1 for the first half, then change it
        // to zero at the exact half. The zero playback rate should hold the
        // sample value of the buffer index at the moment. (sample-and-hold)
        ramp.playbackRate.setValueAtTime(1.0, 0.0);
        ramp.playbackRate.setValueAtTime(0.0, renderDuration / 2);

        context.startRendering()
            .then(function(renderedBuffer) {
              let data = renderedBuffer.getChannelData(0);
              let rampData = rampBuffer.getChannelData(0);
              let half = rampData.length / 2;
              let passed = true;
              let i;

              for (i = 1; i < rampData.length; i++) {
                if (i < half) {
                  // Before the half position, the actual should match with the
                  // original ramp data.
                  if (data[i] !== rampData[i]) {
                    passed = false;
                    break;
                  }
                } else {
                  // From the half position, the actual value should not change.
                  if (data[i] !== rampData[half]) {
                    passed = false;
                    break;
                  }
                }
              }

              should(passed, 'The zero playbackRate')
                  .message(
                      'held the sample value correctly',
                      'should hold the sample value. ' +
                          'Expected ' + rampData[half] + ' but got ' + data[i] +
                          ' at the index ' + i);
            })
            .then(() => task.done());
      });

      audit.define('subsample start with playback rate 0', (task, should) => {
        let context = new OfflineAudioContext(1, renderLength, sampleRate);
        let rampBuffer = new AudioBuffer(
            {length: renderLength, sampleRate: context.sampleRate});
        let data = new Float32Array(renderLength);
        let startValue = 5;
        for (let k = 0; k < data.length; ++k) {
          data[k] = k + startValue;
        }
        rampBuffer.copyToChannel(data, 0);

        let src = new AudioBufferSourceNode(
            context, {buffer: rampBuffer, playbackRate: 0});

        src.connect(context.destination);

        // Purposely start the source between frame boundaries
        let startFrame = 27.3;
        src.start(startFrame / context.sampleRate);

        context.startRendering()
            .then(audioBuffer => {
              let actualStartFrame = Math.ceil(startFrame);
              let audio = audioBuffer.getChannelData(0);

              should(
                  audio.slice(0, actualStartFrame),
                  `output[0:${actualStartFrame - 1}]`)
                  .beConstantValueOf(0);
              should(
                  audio.slice(actualStartFrame), `output[${actualStartFrame}:]`)
                  .beConstantValueOf(startValue);
            })
            .then(() => task.done());
      });

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