<!DOCTYPE html>

<!--
Tests that we are able to schedule a series of notes to playback with sample-accuracy.
We use an impulse so we can tell exactly where the rendering is happening.
-->

<html>
<head>
<script src="../resources/js-test.js"></script>
<script type="text/javascript" src="resources/audio-testing.js"></script>
<script type="text/javascript" src="resources/buffer-loader.js"></script>
</head>

<body>

<div id="description"></div>
<div id="console"></div>

<script>
description("Tests sample-accurate scheduling.");

var sampleRate = 44100.0;
var lengthInSeconds = 4;

var context = 0;
var bufferLoader = 0;
var impulse;

// See if we can render at exactly these sample offsets.
var sampleOffsets = [0, 3, 512, 517, 1000, 1005, 20000, 21234, 37590];

function createImpulse() {
    // An impulse has a value of 1 at time 0, and is otherwise 0.
    impulse = context.createBuffer(2, 512, sampleRate);
    var sampleDataL = impulse.getChannelData(0);
    var sampleDataR = impulse.getChannelData(1);
    sampleDataL[0] = 1.0;
    sampleDataR[0] = 1.0;
}

function playNote(time) {
    var bufferSource = context.createBufferSource();
    bufferSource.buffer = impulse;
    bufferSource.connect(context.destination);
    bufferSource.start(time);
}

function checkSampleAccuracy(event) {
    var buffer = event.renderedBuffer;

    var bufferDataL = buffer.getChannelData(0);
    var bufferDataR = buffer.getChannelData(1);

    var success = true;
    var impulseCount = 0;
    var badOffsetCount = false;
    
    // Go through every sample and make sure it's 0, except at positions in sampleOffsets.
    for (var i = 0; i < buffer.length; ++i) {
        // Make sure left == right
        if (bufferDataL[i] != bufferDataR[i]) {
            testFailed("Rendered buffer left and right channels are not identical.");
            success = false;
            break;
        }
        
        if (bufferDataL[i] != 0) {
            // Make sure this index is  in sampleOffsets
            var found = false;
            for (var j = 0; j < sampleOffsets.length; ++j) {
                if (sampleOffsets[j] == i) {
                    found = true;
                    break;
                }
            }
            ++impulseCount;
            if (!found) {
                testFailed("Non-zero sample found at sample offset " + i);
                success = false;
                ++badOffsetCount;
            }
        }
    }

    if (impulseCount == sampleOffsets.length) {
        if (badOffsetCount == 0) {
            testPassed("Expected number of events found.");
        } else {
            testFailed("Expected number of events found, but " + badOffsetCount + " are at incorrect offsets.");
            success = false;
        }
    } else {
        testFailed("Expected " + sampleOffsets.length + " impulses but only found " + impulseCount);
        success = false;
    }

    if (success) {
        testPassed("All events rendered with sample-accuracy.");
    } else {
        testFailed("Events NOT rendered with sample-accuracy.");
    }

    finishJSTest();
}

function runTest() {
    window.jsTestIsAsync = true;
        
    // Create offline audio context.
    context = new OfflineAudioContext(2, sampleRate * lengthInSeconds, sampleRate);
    createImpulse();    

    for (var i = 0; i < sampleOffsets.length; ++i) {
        var timeInSeconds = sampleOffsets[i] / sampleRate;
        playNote(timeInSeconds);
    }
    
    context.oncomplete = checkSampleAccuracy;
    context.startRendering();
}

runTest();

</script>
</body>
</html>
