| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| Test Automation of AudioListener |
| </title> |
| <script src="../../imported/w3c/web-platform-tests/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; |
| // These tests are quite slow, so don't run for many frames. 1024 frames |
| // should be enough to demonstrate that automations are working. |
| let renderFrames = 1024; |
| let renderDuration = renderFrames / sampleRate; |
| |
| // End time of the automation. Fairly arbitrary, but must be less than |
| // the render duration so that we can verify that the automation did end |
| // at the correct time and value. |
| let endTime = 0.75 * renderDuration; |
| |
| let audit = Audit.createTaskRunner(); |
| |
| // Starting position for the panner |
| let defaultStartPosition = {x: 0, y: 0, z: 0}; |
| |
| // Ending position for the panner |
| let pannerEndPosition = {x: 1000, y: 1000, z: 1000}; |
| |
| // Ending position for the listener. It MUST be the the negative of the |
| // pannerEndPosition! |
| let listenerEndPosition = { |
| x: -pannerEndPosition.x, |
| y: -pannerEndPosition.y, |
| z: -pannerEndPosition.z |
| }; |
| |
| |
| // Test the linear, inverse, and exponential distance models when the |
| // AudioListener is moving instead of the panner. We take advantage that |
| // motion is relative and create a reference by moving the panner in one |
| // direction. The resulting output is the expected result. Then we redo |
| // the test, but this time move the listener in the opposite direction. |
| // The output of this test is compared against the panner result. They |
| // should be the same. |
| |
| audit.define('linear', (task, should) => { |
| runTest({ |
| should: should, |
| startPosition: defaultStartPosition, |
| endPosition: pannerEndPosition, |
| distanceModel: {model: 'linear', rolloff: 1} |
| }).then(() => task.done()); |
| }); |
| |
| audit.define('exponential', (task, should) => { |
| runTest({ |
| should: should, |
| startPosition: defaultStartPosition, |
| endPosition: pannerEndPosition, |
| distanceModel: {model: 'exponential', rolloff: 1.5} |
| }).then(() => task.done()); |
| }); |
| |
| audit.define('inverse', (task, should) => { |
| runTest({ |
| should: should, |
| startPosition: defaultStartPosition, |
| endPosition: pannerEndPosition, |
| distanceModel: {model: 'inverse', rolloff: 1} |
| }).then(() => task.done()); |
| }); |
| |
| audit.run(); |
| |
| function createGraph() { |
| let context = new OfflineAudioContext(2, renderFrames, sampleRate); |
| // Stereo source for the panner. |
| let source = context.createBufferSource(); |
| source.buffer = createConstantBuffer(context, renderFrames, [1, 2]); |
| |
| let panner = context.createPanner(); |
| panner.panningModel = 'equalpower'; |
| |
| source.connect(panner); |
| panner.connect(context.destination); |
| |
| return {context: context, source: source, panner: panner}; |
| } |
| |
| // Run a listener test with the given options. |
| function runTest(options) { |
| let refResult; |
| |
| return runPannerTest(options) |
| .then(function(renderedBuffer) { |
| refResult = renderedBuffer; |
| }) |
| .then(function() { |
| // Move the listener in the opposite direction. |
| options.endPosition = listenerEndPosition; |
| return runListenerTest(options).then(function(renderedBuffer) { |
| compareResults(renderedBuffer, refResult, options); |
| }); |
| }); |
| } |
| |
| function runPannerTest(options) { |
| let graph = createGraph(); |
| |
| return runTestCore(graph, options, graph.panner); |
| } |
| |
| |
| function runListenerTest(options) { |
| let graph = createGraph(); |
| |
| return runTestCore(graph, options, graph.context.listener); |
| } |
| |
| function runTestCore(graph, options, audioOjbect) { |
| let {context, source, panner} = graph; |
| |
| // Initialize the panner with known values. |
| panner.distanceModel = options.distanceModel.model; |
| panner.rolloffFactor = options.distanceModel.rolloff; |
| panner.panningModel = 'equalpower'; |
| |
| // Automate the location. audioObject must be either a PannerNode or |
| // the context's AudioListener. |
| |
| audioOjbect.positionX.setValueAtTime(options.startPosition.x, 0); |
| audioOjbect.positionY.setValueAtTime(options.startPosition.y, 0); |
| audioOjbect.positionZ.setValueAtTime(options.startPosition.z, 0); |
| |
| audioOjbect.positionX.linearRampToValueAtTime( |
| options.endPosition.x, endTime); |
| audioOjbect.positionY.linearRampToValueAtTime( |
| options.endPosition.y, endTime); |
| audioOjbect.positionZ.linearRampToValueAtTime( |
| options.endPosition.z, endTime); |
| |
| // Start the source, render the graph, and return the resulting promise |
| // from rendering. |
| source.start(); |
| |
| return context.startRendering().then(function(resultBuffer) { |
| return resultBuffer; |
| }); |
| } |
| |
| function compareResults(actualResult, expectedResult, options) { |
| // Compare the output with the reference output from moving the source |
| // in the opposite direction. |
| |
| let expectedLeft = expectedResult.getChannelData(0); |
| let expectedRight = expectedResult.getChannelData(1); |
| |
| let actualLeft = actualResult.getChannelData(0); |
| let actualRight = actualResult.getChannelData(1); |
| |
| let prefix = 'Distance model: "' + options.distanceModel.model + '"'; |
| prefix += ', rolloff: ' + options.distanceModel.rolloff; |
| options.should(actualLeft, prefix + ': left channel') |
| .beCloseToArray(expectedLeft, 0); |
| options.should(actualRight, prefix + ': right channel') |
| .beCloseToArray(expectedRight, 0); |
| } |
| </script> |
| </body> |
| </html> |