<!DOCTYPE html>
<html>
<head>
<title>
Test Analyser.getByteTimeDomainData()
</title>
<script src="../../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">
let sampleRate = 48000;
// The size of the analyser frame. Anything larger than 128 is ok, but
// should be long enough to capture the peaks of the oscillator waveform.
let fftSize = 256;
// Number of frames to render. Should be greater than the fftSize, but is
// otherwise arbitrary.
let renderFrames = 2 * fftSize;
let audit = Audit.createTaskRunner();
// Test that getByteTimeDomainData returns the correct values. This test
// depends on getFloatTimeDomainData returning the correct data (for which
// there is already a test).
audit.define('byte-data', (task, should) => {
let context = new OfflineAudioContext(1, renderFrames, sampleRate);
// Create a sawtooth as the signal under test. A sine wave or triangle
// wave would probably also work.
let src = context.createOscillator();
src.type = 'sawtooth';
// Choose a frequency high enough that we get at least a full period in
// one analyser fftSize frame. Otherwise, the frequency is arbitrary.
src.frequency.value = 440;
// Gain node to make sure the signal goes somewhat above 1, for testing
// clipping.
let gain = context.createGain();
gain.gain.value = 1.5;
// The analyser node to test
let analyser = context.createAnalyser();
analyser.fftSize = fftSize;
// Connect the graph.
src.connect(gain);
gain.connect(analyser);
analyser.connect(context.destination);
// Stop rendering after one analyser frame so we can grab the data.
context.suspend(fftSize / sampleRate)
.then(function() {
let floatData = new Float32Array(fftSize);
let byteData = new Uint8Array(fftSize);
analyser.getFloatTimeDomainData(floatData);
analyser.getByteTimeDomainData(byteData);
// Use the float data to compute the expected value for the byte
// data.
let expected = new Float32Array(fftSize);
for (let k = 0; k < fftSize; ++k) {
// It's important to do Math.fround to match the
// single-precision float in the implementation!
let value = Math.fround(128 * Math.fround(1 + floatData[k]));
// Clip the result to lie in the range [0, 255].
expected[k] = Math.floor(Math.min(255, Math.max(0, value)));
}
// Find the first index of the first sample that exceeds +1 or -1.
// The test MUST have at least one such value.
let indexMax = floatData.findIndex(function(x) {
return x > 1;
});
let indexMin = floatData.findIndex(function(x) {
return x < -1;
});
should(indexMax, 'Index of first sample greater than +1')
.beGreaterThanOrEqualTo(0);
should(indexMin, 'Index of first sample less than -1')
.beGreaterThanOrEqualTo(0);
// Verify explicitly that clipping happened correctly at the above
// indices.
should(
byteData[indexMax],
'Clip ' + floatData[indexMax].toPrecision(6) +
': byteData[' + indexMax + ']')
.beEqualTo(255);
should(
byteData[indexMin],
'Clip ' + floatData[indexMin].toPrecision(6) + ': byteData[' +
indexMin + ']')
.beEqualTo(0);
// Verify that all other samples are computed correctly.
should(byteData, 'Byte data').beEqualToArray(expected);
})
.then(context.resume.bind(context))
src.start();
context.startRendering().then(() => task.done());
});
audit.run();
</script>
</body>
</html>