| <!doctype html> |
| <html> |
| <head> |
| <script src="../resources/js-test.js"></script> |
| <script src="resources/compatibility.js"></script> |
| <script src="resources/audio-testing.js"></script> |
| <title>Test Analyser.getByteTimeDomainData()</title> |
| </head> |
| |
| <body> |
| <script> |
| description("Test AnalyserNode getByteTimeDomainData"); |
| window.jsTestIsAsync = true; |
| |
| var 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. |
| var fftSize = 256; |
| // Number of frames to render. Should be greater than the fftSize, but is otherwise |
| // arbitrary. |
| var renderFrames = 2 * fftSize; |
| |
| var 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.defineTask("byte-data", function (done) { |
| var context = new OfflineAudioContext(1, renderFrames, sampleRate); |
| |
| // Create a sawtooth as the signal under test. A sine wave or triangle wave would probably |
| // also work. |
| var 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. |
| var gain = context.createGain(); |
| gain.gain.value = 1.5; |
| |
| // The analyser node to test |
| var 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 () { |
| var floatData = new Float32Array(fftSize); |
| var byteData = new Uint8Array(fftSize); |
| |
| analyser.getFloatTimeDomainData(floatData); |
| analyser.getByteTimeDomainData(byteData); |
| |
| // Use the float data to compute the expected value for the byte data. |
| var expected = new Float32Array(fftSize); |
| for (var k = 0; k < fftSize; ++k) { |
| // It's important to do Math.fround to match the single-precision float in the |
| // implementation! |
| var 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. |
| var indexMax = floatData.findIndex(function (x) { return x > 1; }); |
| var indexMin = floatData.findIndex(function (x) { return x < -1; }); |
| |
| Should("Index of first sample greater than +1", indexMax).beGreaterThanOrEqualTo(0); |
| Should("Index of first sample less than -1", indexMin).beGreaterThanOrEqualTo(0); |
| |
| // Verify explicitly that clipping happened correctly at the above indices. |
| Should("Clip " + floatData[indexMax].toPrecision(6) + ": byteData[" + indexMax + "]", |
| byteData[indexMax]).beEqualTo(255); |
| Should("Clip " + floatData[indexMin].toPrecision(6) + ": byteData[" + indexMin + "]", |
| byteData[indexMin]).beEqualTo(0); |
| |
| // Verify that all other samples are computed correctly. |
| Should("Byte data", byteData, { |
| verbose: true |
| }).beEqualToArray(expected); |
| }).then(context.resume.bind(context)) |
| |
| src.start(); |
| context.startRendering().then(done); |
| }); |
| |
| audit.defineTask("finish", function (done) { |
| finishJSTest(); |
| done(); |
| }); |
| |
| audit.runTasks(); |
| </script> |
| </body> |
| </html> |