<!DOCTYPE html>
<title>Test effect of band limiting on PeriodicWave signals</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const sampleRate = 48000;
const bufferSize = 12800;
const epsilon = 0.01;

// "All implementations must support arrays up to at least 8192", but the
// linear interpolation of the current implementation distorts the higher
// frequency components too much to pass this test.
const frequencyIndexMax = 200;

// A set of oscillators are created near the Nyquist frequency.
// These are factors giving each oscillator frequency relative to the Nyquist.
// The first is an octave below Nyquist and the last is just above.
const OCTAVE_BELOW = 0;
const HALF_BELOW = 1;
const NEAR_BELOW = 2;
const ABOVE = 3;
const oscillatorFactors = [0.5, Math.sqrt(0.5), 0.99, 1.01];
const oscillatorCount = oscillatorFactors.length;

// Return magnitude relative to unit sine wave
function magnitude(array) {
  var mag = 0
  for (var i = 0; i < array.length; ++i) {
    sample = array[i];
    mag += sample * sample;
  }
  return Math.sqrt(2 * mag / array.length);
}

function test_frequency_index(frequencyIndex) {

  var context =
    new OfflineAudioContext(oscillatorCount, bufferSize, sampleRate);

  var merger = context.createChannelMerger(oscillatorCount);
  merger.connect(context.destination);

  var real = new Float32Array(frequencyIndex + 1);
  real[frequencyIndex] = 1;
  var image = new Float32Array(real.length);
  var wave = context.createPeriodicWave(real, image);

  for (var i = 0; i < oscillatorCount; ++i) {
    var oscillator = context.createOscillator();
    oscillator.frequency.value =
      oscillatorFactors[i] * sampleRate / (2 * frequencyIndex);
    oscillator.connect(merger, 0, i);
    oscillator.setPeriodicWave(wave);
    oscillator.start(0);
  }

  return context.startRendering().
    then((buffer) => {
      assert_equals(buffer.numberOfChannels, oscillatorCount);
      var magnitudes = [];
      for (var i = 0; i < oscillatorCount; ++i) {
        magnitudes[i] = magnitude(buffer.getChannelData(i));
      }
      // Unaffected by band-limiting one octave below Nyquist.
      assert_approx_equals(magnitudes[OCTAVE_BELOW], 1, epsilon,
                           "magnitude with frequency octave below Nyquist");
      // Still at least half the amplitude at half octave below Nyquist.
      assert_greater_than(magnitudes[HALF_BELOW], 0.5 * (1 - epsilon),
                          "magnitude with frequency half octave below Nyquist");
      // Approaching zero or zero near Nyquist.
      assert_less_than(magnitudes[NEAR_BELOW], 0.1,
                       "magnitude with frequency near Nyquist");
      assert_equals(magnitudes[ABOVE], 0,
                   "magnitude with frequency above Nyquist");
    });
}

// The 5/4 ratio with rounding up provides sampling across a range of
// octaves and offsets within octaves.
for (var frequencyIndex = 1;
     frequencyIndex < frequencyIndexMax;
     frequencyIndex = Math.floor((5 * frequencyIndex + 3) / 4)) {
  promise_test(test_frequency_index.bind(null, frequencyIndex),
               "Frequency " + frequencyIndex);
}
</script>
