Bug 1442809 [wpt PR 9798] - Upstream ConvolverNode tests to WPT, a=testonly

Automatic update from web-platform-testsUpstream ConvolverNode tests to WPT

Add thresholds to allow for differences in expected results.

Needed for Firefox to pass the tests.  Thresholds were set using
Firefox on Linux.  Where the tests pass, the output looks correct
except for small numerical differences.

Bug: 745778
Change-Id: I8fa6582ac960922e777d83d9debc71aa36e1741b
Reviewed-on: https://chromium-review.googlesource.com/947096
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
Commit-Queue: Raymond Toy <rtoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#541318}

wpt-commits: 16376ce7a6ca10ca6ac31dff565c47b6555e3103
wpt-pr: 9798
wpt-commits: 16376ce7a6ca10ca6ac31dff565c47b6555e3103
wpt-pr: 9798
This commit is contained in:
Raymond Toy 2018-03-26 15:18:15 +00:00 committed by James Graham
parent 0eb8f93dd2
commit 4e4a1dc9bb
12 changed files with 1204 additions and 6 deletions

View File

@ -106245,6 +106245,18 @@
{}
]
],
"css/css-counter-styles/broken-symbols.htm": [
[
"/css/css-counter-styles/broken-symbols.htm",
[
[
"/css/css-counter-styles/broken-symbols-ref.htm",
"=="
]
],
{}
]
],
"css/css-display/display-contents-alignment-001.html": [
[
"/css/css-display/display-contents-alignment-001.html",
@ -237398,6 +237410,11 @@
{}
]
],
"css/css-counter-styles/broken-symbols-ref.htm": [
[
{}
]
],
"css/css-display/OWNERS": [
[
{}
@ -294288,6 +294305,11 @@
{}
]
],
"webaudio/resources/convolution-testing.js": [
[
{}
]
],
"webaudio/resources/delay-testing.js": [
[
{}
@ -363605,6 +363627,48 @@
{}
]
],
"webaudio/the-audio-api/the-convolvernode-interface/convolution-mono-mono.html": [
[
"/webaudio/the-audio-api/the-convolvernode-interface/convolution-mono-mono.html",
{}
]
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-cascade.html": [
[
"/webaudio/the-audio-api/the-convolvernode-interface/convolver-cascade.html",
{}
]
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-channels.html": [
[
"/webaudio/the-audio-api/the-convolvernode-interface/convolver-channels.html",
{}
]
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-response-1-chan.html": [
[
"/webaudio/the-audio-api/the-convolvernode-interface/convolver-response-1-chan.html",
{}
]
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-response-2-chan.html": [
[
"/webaudio/the-audio-api/the-convolvernode-interface/convolver-response-2-chan.html",
{}
]
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-response-4-chan.html": [
[
"/webaudio/the-audio-api/the-convolvernode-interface/convolver-response-4-chan.html",
{}
]
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-setBuffer-null.html": [
[
"/webaudio/the-audio-api/the-convolvernode-interface/convolver-setBuffer-null.html",
{}
]
],
"webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html": [
[
"/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html",
@ -485674,6 +485738,14 @@
"dbfaae0538c8cd1e759b59712f53cd17388e7c48",
"manual"
],
"css/css-counter-styles/broken-symbols-ref.htm": [
"c794c4b6846212f4ed27e618cdd32a28b8ce013c",
"support"
],
"css/css-counter-styles/broken-symbols.htm": [
"23cb132e8efcdd1020e12c89c9d30a333708b72e",
"reftest"
],
"css/css-counter-styles/cambodian/css3-counter-styles-158.html": [
"a334bd087e7a808567daeeae419a80466a377ef3",
"manual"
@ -568975,7 +569047,7 @@
"support"
],
"html/syntax/parsing/the-end.html": [
"78b17f053dd3e52b3bf68a5fafc4a4e070e65cfd",
"4b38fe0c7bcb088450cc13f13c805711caf91961",
"testharness"
],
"html/syntax/serializing-html-fragments/.gitkeep": [
@ -599222,6 +599294,10 @@
"914a0268776de98f3b608babfa5699a93a2cd723",
"support"
],
"webaudio/resources/convolution-testing.js": [
"4a0ae6b3701147d16cb91e5660e1f4909d022d06",
"support"
],
"webaudio/resources/delay-testing.js": [
"aeb4554e50589738c94600cc2ed1e84be7381cac",
"support"
@ -599458,6 +599534,34 @@
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"support"
],
"webaudio/the-audio-api/the-convolvernode-interface/convolution-mono-mono.html": [
"fe5a5ea090d9e23400f0f1c8655b952a6cd922cb",
"testharness"
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-cascade.html": [
"e314bbc4e4baad7eddcd6fe279ca1e1b73f25e88",
"testharness"
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-channels.html": [
"0c8c70ead1c6f71970bfb608bb1385c4eea144e4",
"testharness"
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-response-1-chan.html": [
"542d021100cd7b6bbb2ab834d36964d7685da7a3",
"testharness"
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-response-2-chan.html": [
"07f964666ccce306fc22a0502e9edf345c7e635d",
"testharness"
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-response-4-chan.html": [
"8fac11e0ecfa8bfb9b49d68d0792793f44e94ad4",
"testharness"
],
"webaudio/the-audio-api/the-convolvernode-interface/convolver-setBuffer-null.html": [
"f32f5acdf031b1a2b32bc37324b105d1df7c5fdb",
"testharness"
],
"webaudio/the-audio-api/the-delaynode-interface/.gitkeep": [
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"support"

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="UTF-8">
<ol><li>Should have "1." as bullet point.</ol>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta charset="UTF-8">
<title>CSS Test: invalid counter-style symbols</title>
<link rel="author" title="Anthony Ramine" href="mailto:n.oxyde@gmail.com">
<link rel="help" href="https://drafts.csswg.org/css-counter-styles-3/#typedef-symbol">
<link rel="match" href="broken-symbols-ref.htm">
<style type="text/css">
@counter-style a {
system: alphabetic;
symbols: ⓐ inherit;
}
</style>
<ol style="list-style-type: a"><li>Should have "1." as bullet point.</ol>

View File

@ -29,12 +29,28 @@ async_test(function() {
}, "load");
async_test(function() {
var seen = false;
document.addEventListener("DOMContentLoaded", this.step_func(function() {
seen = true;
window.addEventListener("pageshow", this.step_func_done(function(e) {
assert_equals(e.type, "pageshow");
assert_false(e.bubbles, "bubbles should be false");
assert_false(e.cancelable, "cancelable should be false");
assert_equals(e.target, document, "target should be document");
assert_true(e.isTrusted, "isTrusted should be true");
assert_class_string(e, "PageTransitionEvent");
}));
window.addEventListener("load", this.step_func_done(function() {
assert_true(seen, "DOMContentLoaded should be fired before load");
}, "pageshow");
async_test(function() {
var seen_dcl = false;
var seen_load = false;
document.addEventListener("DOMContentLoaded", this.step_func(function() {
seen_dcl = true;
}));
window.addEventListener("load", this.step_func(function() {
seen_load = true;
assert_true(seen_dcl, "DOMContentLoaded should be fired before load");
}));
window.addEventListener("pageshow", this.step_func_done(function() {
assert_true(seen_load, "load should be fired before pageshow")
}));
}, "order");
</script>

View File

@ -0,0 +1,168 @@
let sampleRate = 44100.0;
let renderLengthSeconds = 8;
let pulseLengthSeconds = 1;
let pulseLengthFrames = pulseLengthSeconds * sampleRate;
function createSquarePulseBuffer(context, sampleFrameLength) {
let audioBuffer =
context.createBuffer(1, sampleFrameLength, context.sampleRate);
let n = audioBuffer.length;
let data = audioBuffer.getChannelData(0);
for (let i = 0; i < n; ++i)
data[i] = 1;
return audioBuffer;
}
// The triangle buffer holds the expected result of the convolution.
// It linearly ramps up from 0 to its maximum value (at the center)
// then linearly ramps down to 0. The center value corresponds to the
// point where the two square pulses overlap the most.
function createTrianglePulseBuffer(context, sampleFrameLength) {
let audioBuffer =
context.createBuffer(1, sampleFrameLength, context.sampleRate);
let n = audioBuffer.length;
let halfLength = n / 2;
let data = audioBuffer.getChannelData(0);
for (let i = 0; i < halfLength; ++i)
data[i] = i + 1;
for (let i = halfLength; i < n; ++i)
data[i] = n - i - 1;
return audioBuffer;
}
function log10(x) {
return Math.log(x) / Math.LN10;
}
function linearToDecibel(x) {
return 20 * log10(x);
}
// Verify that the rendered result is very close to the reference
// triangular pulse.
function checkTriangularPulse(rendered, reference, should) {
let match = true;
let maxDelta = 0;
let valueAtMaxDelta = 0;
let maxDeltaIndex = 0;
for (let i = 0; i < reference.length; ++i) {
let diff = rendered[i] - reference[i];
let x = Math.abs(diff);
if (x > maxDelta) {
maxDelta = x;
valueAtMaxDelta = reference[i];
maxDeltaIndex = i;
}
}
// allowedDeviationFraction was determined experimentally. It
// is the threshold of the relative error at the maximum
// difference between the true triangular pulse and the
// rendered pulse.
let allowedDeviationDecibels = -124.41;
let maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta);
should(
maxDeviationDecibels,
'Deviation (in dB) of triangular portion of convolution')
.beLessThanOrEqualTo(allowedDeviationDecibels);
return match;
}
// Verify that the rendered data is close to zero for the first part
// of the tail.
function checkTail1(data, reference, breakpoint, should) {
let isZero = true;
let tail1Max = 0;
for (let i = reference.length; i < reference.length + breakpoint; ++i) {
let mag = Math.abs(data[i]);
if (mag > tail1Max) {
tail1Max = mag;
}
}
// Let's find the peak of the reference (even though we know a
// priori what it is).
let refMax = 0;
for (let i = 0; i < reference.length; ++i) {
refMax = Math.max(refMax, Math.abs(reference[i]));
}
// This threshold is experimentally determined by examining the
// value of tail1MaxDecibels.
let threshold1 = -129.7;
let tail1MaxDecibels = linearToDecibel(tail1Max / refMax);
should(tail1MaxDecibels, 'Deviation in first part of tail of convolutions')
.beLessThanOrEqualTo(threshold1);
return isZero;
}
// Verify that the second part of the tail of the convolution is
// exactly zero.
function checkTail2(data, reference, breakpoint, should) {
let isZero = true;
let tail2Max = 0;
// For the second part of the tail, the maximum value should be
// exactly zero.
let threshold2 = 0;
for (let i = reference.length + breakpoint; i < data.length; ++i) {
if (Math.abs(data[i]) > 0) {
isZero = false;
break;
}
}
should(isZero, 'Rendered signal after tail of convolution is silent')
.beTrue();
return isZero;
}
function checkConvolvedResult(renderedBuffer, trianglePulse, should) {
let referenceData = trianglePulse.getChannelData(0);
let renderedData = renderedBuffer.getChannelData(0);
let success = true;
// Verify the triangular pulse is actually triangular.
success =
success && checkTriangularPulse(renderedData, referenceData, should);
// Make sure that portion after convolved portion is totally
// silent. But round-off prevents this from being completely
// true. At the end of the triangle, it should be close to
// zero. If we go farther out, it should be even closer and
// eventually zero.
// For the tail of the convolution (where the result would be
// theoretically zero), we partition the tail into two
// parts. The first is the at the beginning of the tail,
// where we tolerate a small but non-zero value. The second part is
// farther along the tail where the result should be zero.
// breakpoint is the point dividing the first two tail parts
// we're looking at. Experimentally determined.
let breakpoint = 12800;
success =
success && checkTail1(renderedData, referenceData, breakpoint, should);
success =
success && checkTail2(renderedData, referenceData, breakpoint, should);
should(success, 'Test signal convolved').message('correctly', 'incorrectly');
}

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<title>
convolution-mono-mono.html
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
<script src="/webaudio/resources/convolution-testing.js"></script>
</head>
<body>
<script id="layout-test-code">
let audit = Audit.createTaskRunner();
// description("Tests ConvolverNode processing a mono channel with mono
// impulse response.");
// To test the convolver, we convolve two square pulses together to
// produce a triangular pulse. To verify the result is correct we
// check several parts of the result. First, we make sure the initial
// part of the result is zero (due to the latency in the convolver).
// Next, the triangular pulse should match the theoretical result to
// within some roundoff. After the triangular pulse, the result
// should be exactly zero, but round-off prevents that. We make sure
// the part after the pulse is sufficiently close to zero. Finally,
// the result should be exactly zero because the inputs are exactly
// zero.
audit.define('test', function(task, should) {
// Create offline audio context.
let context = new OfflineAudioContext(
2, sampleRate * renderLengthSeconds, sampleRate);
let squarePulse = createSquarePulseBuffer(context, pulseLengthFrames);
let trianglePulse =
createTrianglePulseBuffer(context, 2 * pulseLengthFrames);
let bufferSource = context.createBufferSource();
bufferSource.buffer = squarePulse;
let convolver = context.createConvolver();
convolver.normalize = false;
convolver.buffer = squarePulse;
bufferSource.connect(convolver);
convolver.connect(context.destination);
bufferSource.start(0);
context.startRendering()
.then(buffer => {
checkConvolvedResult(buffer, trianglePulse, should);
})
.then(task.done.bind(task));
;
});
audit.run();
</script>
</body>
</html>

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<title>
Test Cascade of Mono Convolvers
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
let audit = Audit.createTaskRunner();
// Arbitrary sample rate and reasonably short duration
let sampleRate = 8000;
let duration = 0.25;
let renderFrames = duration * sampleRate;
audit.define(
{label: 'cascade-mono', description: 'Cascaded mono convolvers'},
(task, should) => {
// Cascade two convolvers with mono responses and verify that the
// output is not silent.
let context = new OfflineAudioContext(1, renderFrames, sampleRate);
let b0 =
new AudioBuffer({length: 5, sampleRate: context.sampleRate});
b0.getChannelData(0)[1] = 1;
let c0 = new ConvolverNode(
context, {disableNormalization: true, buffer: b0});
let b1 =
new AudioBuffer({length: 5, sampleRate: context.sampleRate});
b1.getChannelData(0)[2] = 1;
let c1 = new ConvolverNode(
context, {disableNormalization: true, buffer: b1});
let src = new OscillatorNode(context);
src.connect(c0).connect(c1).connect(context.destination);
src.start();
context.startRendering()
.then(audioBuffer => {
// Just verify the output is not silent
let audio = audioBuffer.getChannelData(0);
should(audio, 'Output of cascaded mono convolvers')
.notBeConstantValueOf(0);
})
.then(() => task.done());
});
audit.run();
</script>
</body>
</html>

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<title>
Test Supported Number of Channels for ConvolverNode
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
let audit = Audit.createTaskRunner();
audit.define('channel-count-test', (task, should) => {
// Just need a context to create nodes on, so any allowed length and
// rate is ok.
let context = new OfflineAudioContext(1, 1, 48000);
let success = true;
for (let count = 1; count <= 32; ++count) {
let convolver = context.createConvolver();
let buffer = context.createBuffer(count, 1, context.sampleRate);
let message = 'ConvolverNode with buffer of ' + count + ' channels';
if (count == 1 || count == 2 || count == 4) {
// These are the only valid channel counts for the buffer.
should(() => convolver.buffer = buffer, message).notThrow();
} else {
should(() => convolver.buffer = buffer, message)
.throw('NotSupportedError');
}
}
task.done();
});
audit.run();
</script>
</body>
</html>

View File

@ -0,0 +1,232 @@
<!DOCTYPE html>
<html>
<head>
<title>
Test Convolver Channel Outputs for Response with 1 channel
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
// Test various convolver configurations when the convolver response has
// one channel (mono).
//
// Fairly arbitrary sample rate, except that we want the rate to be a
// power of two so that 1/sampleRate is exactly respresentable as a
// single-precision float.
let sampleRate = 8192;
// A fairly arbitrary number of frames, except the number of frames should
// be more than a few render quanta.
let renderFrames = 10 * 128;
let audit = Audit.createTaskRunner();
// Convolver response
let response;
audit.define(
{
label: 'initialize',
description: 'Convolver response with one channel'
},
(task, should) => {
// Convolver response
should(
() => {
response = new AudioBuffer(
{numberOfChannels: 1, length: 2, sampleRate: sampleRate});
response.getChannelData(0)[1] = 1;
},
'new AudioBuffer({numberOfChannels: 1, length: 2, sampleRate: ' +
sampleRate + '})')
.notThrow();
task.done();
});
audit.define(
{label: '1-channel input', description: 'produces 1-channel output'},
(task, should) => {
// Create a 3-channel context: channel 0 = convolver under test,
// channel 1: test that convolver output is not stereo, channel 2:
// expected output. The context MUST be discrete so that the
// channels don't get mixed in some unexpected way.
let context = new OfflineAudioContext(3, renderFrames, sampleRate);
context.destination.channelInterpretation = 'discrete';
let src = new OscillatorNode(context);
let conv = new ConvolverNode(
context, {disableNormalization: true, buffer: response});
// Splitter node to verify that the output of the convolver is mono.
// channelInterpretation must be 'discrete' so we don't do any
// mixing of the input to the node.
let splitter = new ChannelSplitterNode(
context,
{numberOfOutputs: 2, channelInterpretation: 'discrete'});
// Final merger to feed all of the individual channels into the
// destination.
let merger = new ChannelMergerNode(context, {numberOfInputs: 3});
src.connect(conv).connect(splitter);
splitter.connect(merger, 0, 0);
splitter.connect(merger, 1, 1);
// The convolver response is a 1-sample delay. Use a delay node to
// implement this.
let delay =
new DelayNode(context, {delayTime: 1 / context.sampleRate});
src.connect(delay);
delay.connect(merger, 0, 2);
merger.connect(context.destination);
src.start();
context.startRendering()
.then(audioBuffer => {
// Extract out the three channels
let actual = audioBuffer.getChannelData(0);
let c1 = audioBuffer.getChannelData(1);
let expected = audioBuffer.getChannelData(2);
// c1 is expected to be zero.
should(c1, '1: Channel 1').beConstantValueOf(0);
// The expected and actual results should be identical
should(actual, 'Convolver output')
.beCloseToArray(expected, {absoluteThreshold: 4.1724e-7});
})
.then(() => task.done());
});
audit.define(
{label: '2-channel input', description: 'produces 2-channel output'},
(task, should) => {
downMixTest({numberOfInputs: 2, prefix: '2'}, should)
.then(() => task.done());
});
audit.define(
{
label: '3-channel input',
description: '3->2 downmix producing 2-channel output'
},
(task, should) => {
downMixTest({numberOfInputs: 3, prefix: '3'}, should)
.then(() => task.done());
});
audit.define(
{
label: '4-channel input',
description: '4->2 downmix producing 2-channel output'
},
(task, should) => {
downMixTest({numberOfInputs: 4, prefix: '4'}, should)
.then(() => task.done());
});
audit.define(
{
label: '5.1-channel input',
description: '5.1->2 downmix producing 2-channel output'
},
(task, should) => {
downMixTest({numberOfInputs: 6, prefix: '5.1'}, should)
.then(() => task.done());
});
function downMixTest(options, should) {
// Create an 4-channel offline context. The first two channels are for
// the stereo output of the convolver and the next two channels are for
// the reference stereo signal.
let context = new OfflineAudioContext(4, renderFrames, sampleRate);
context.destination.channelInterpretation = 'discrete';
// Create oscillators for use as the input. The type and frequency is
// arbitrary except that oscillators must be different.
let src = new Array(options.numberOfInputs);
for (let k = 0; k < src.length; ++k) {
src[k] = new OscillatorNode(
context, {type: 'square', frequency: 440 + 220 * k});
}
// Merger to combine the oscillators into one output stream.
let srcMerger =
new ChannelMergerNode(context, {numberOfInputs: src.length});
for (let k = 0; k < src.length; ++k) {
src[k].connect(srcMerger, 0, k);
}
// Convolver under test.
let conv = new ConvolverNode(
context, {disableNormalization: true, buffer: response});
srcMerger.connect(conv);
// Splitter to get individual channels of the convolver output so we can
// feed them (eventually) to the context in the right set of channels.
let splitter = new ChannelSplitterNode(context, {numberOfOutputs: 2});
conv.connect(splitter);
// Reference graph consists of a delay node to simulate the response of
// the convolver. (The convolver response is designed this way.)
let delay = new DelayNode(context, {delayTime: 1 / context.sampleRate});
// Gain node to mix the sources to stereo in the desired way. (Could be
// done in the delay node, but let's keep the mixing separated from the
// functionality.)
let gainMixer = new GainNode(
context, {channelCount: 2, channelCountMode: 'explicit'});
srcMerger.connect(gainMixer);
// Splitter to extract the channels of the reference signal.
let refSplitter =
new ChannelSplitterNode(context, {numberOfOutputs: 2});
gainMixer.connect(delay).connect(refSplitter);
// Final merger to bring back the individual channels from the convolver
// and the reference in the right order for the destination.
let finalMerger = new ChannelMergerNode(
context, {numberOfInputs: context.destination.channelCount});
// First two channels are for the convolver output, and the next two are
// for the reference.
splitter.connect(finalMerger, 0, 0);
splitter.connect(finalMerger, 1, 1);
refSplitter.connect(finalMerger, 0, 2);
refSplitter.connect(finalMerger, 1, 3);
finalMerger.connect(context.destination);
// Start the sources at last.
for (let k = 0; k < src.length; ++k) {
src[k].start();
}
return context.startRendering().then(audioBuffer => {
// Extract the various channels out
let actual0 = audioBuffer.getChannelData(0);
let actual1 = audioBuffer.getChannelData(1);
let expected0 = audioBuffer.getChannelData(2);
let expected1 = audioBuffer.getChannelData(3);
// Verify that each output channel of the convolver matches
// the delayed signal from the reference
should(actual0, options.prefix + ': Channel 0')
.beEqualToArray(expected0);
should(actual1, options.prefix + ': Channel 1')
.beEqualToArray(expected1);
});
}
audit.run();
</script>
</body>
</html>

View File

@ -0,0 +1,204 @@
<!DOCTYPE html>
<html>
<head>
<title>
Test Convolver Channel Outputs for Response with 2 channels
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
// Test various convolver configurations when the convolver response has
// a stereo response.
// Fairly arbitrary sample rate, except that we want the rate to be a
// power of two so that 1/sampleRate is exactly respresentable as a
// single-precision float.
let sampleRate = 8192;
// A fairly arbitrary number of frames, except the number of frames should
// be more than a few render quanta.
let renderFrames = 10 * 128;
let audit = Audit.createTaskRunner();
// Convolver response
let response;
audit.define(
{
label: 'initialize',
description: 'Convolver response with one channel'
},
(task, should) => {
// Convolver response
should(
() => {
response = new AudioBuffer(
{numberOfChannels: 2, length: 4, sampleRate: sampleRate});
// Each channel of the response is a simple impulse (with
// different delay) so that we can use a DelayNode to simulate
// the convolver output. Channel k is delayed by k+1 frames.
for (let k = 0; k < response.numberOfChannels; ++k) {
response.getChannelData(k)[k + 1] = 1;
}
},
'new AudioBuffer({numberOfChannels: 2, length: 4, sampleRate: ' +
sampleRate + '})')
.notThrow();
task.done();
});
audit.define(
{label: '1-channel input', description: 'produces 2-channel output'},
(task, should) => {
stereoResponseTest({numberOfInputs: 1, prefix: '1', absoluteThresholds:
[3.5763e-7, 4.7684e-7]}, should)
.then(() => task.done());
});
audit.define(
{label: '2-channel input', description: 'produces 2-channel output'},
(task, should) => {
stereoResponseTest({numberOfInputes: 2, prefix: '2', absoluteThresholds:
[3.5763e-7, 4.7684e-7]}, should)
.then(() => task.done());
});
audit.define(
{
label: '3-channel input',
description: '3->2 downmix producing 2-channel output'
},
(task, should) => {
stereoResponseTest({numberOfInputs: 3, prefix: '3', absoluteThresholds:
[3.5763e-7, 3.5763e-7]}, should)
.then(() => task.done());
});
audit.define(
{
label: '4-channel input',
description: '4->2 downmix producing 2-channel output'
},
(task, should) => {
stereoResponseTest({numberOfInputs: 4, prefix: '4', absoluteThresholds:
[3.5763e-7, 2.9803e-7]}, should)
.then(() => task.done());
});
audit.define(
{
label: '5.1-channel input',
description: '5.1->2 downmix producing 2-channel output'
},
(task, should) => {
stereoResponseTest({numberOfInputs: 6, prefix: '5.1', absoluteThresholds:
[7.1526e-7, 7.1526e-7]}, should)
.then(() => task.done());
});
function stereoResponseTest(options, should) {
// Create an 4-channel offline context. The first two channels are for
// the stereo output of the convolver and the next two channels are for
// the reference stereo signal.
let context = new OfflineAudioContext(4, renderFrames, sampleRate);
context.destination.channelInterpretation = 'discrete';
// Create oscillators for use as the input. The type and frequency is
// arbitrary except that oscillators must be different.
let src = new Array(options.numberOfInputs);
for (let k = 0; k < src.length; ++k) {
src[k] = new OscillatorNode(
context, {type: 'square', frequency: 440 + 220 * k});
}
// Merger to combine the oscillators into one output stream.
let srcMerger =
new ChannelMergerNode(context, {numberOfInputs: src.length});
for (let k = 0; k < src.length; ++k) {
src[k].connect(srcMerger, 0, k);
}
// Convolver under test.
let conv = new ConvolverNode(
context, {disableNormalization: true, buffer: response});
srcMerger.connect(conv);
// Splitter to get individual channels of the convolver output so we can
// feed them (eventually) to the context in the right set of channels.
let splitter = new ChannelSplitterNode(context, {numberOfOutputs: 2});
conv.connect(splitter);
// Reference graph consists of a delays node to simulate the response of
// the convolver. (The convolver response is designed this way.)
let delay = new Array(2);
for (let k = 0; k < delay.length; ++k) {
delay[k] = new DelayNode(context, {
delayTime: (k + 1) / context.sampleRate,
channelCount: 1,
channelCountMode: 'explicit'
});
}
// Gain node to mix the sources to stereo in the desired way. (Could be
// done in the delay node, but let's keep the mixing separated from the
// functionality.)
let gainMixer = new GainNode(
context, {channelCount: 2, channelCountMode: 'explicit'});
srcMerger.connect(gainMixer);
// Splitter to extract the channels of the reference signal.
let refSplitter =
new ChannelSplitterNode(context, {numberOfOutputs: 2});
gainMixer.connect(refSplitter);
// Connect each channel to the delay nodes
for (let k = 0; k < delay.length; ++k) {
refSplitter.connect(delay[k], k);
}
// Final merger to bring back the individual channels from the convolver
// and the reference in the right order for the destination.
let finalMerger = new ChannelMergerNode(
context, {numberOfInputs: context.destination.channelCount});
// First two channels are for the convolver output, and the next two are
// for the reference.
splitter.connect(finalMerger, 0, 0);
splitter.connect(finalMerger, 1, 1);
delay[0].connect(finalMerger, 0, 2);
delay[1].connect(finalMerger, 0, 3);
finalMerger.connect(context.destination);
// Start the sources at last.
for (let k = 0; k < src.length; ++k) {
src[k].start();
}
return context.startRendering().then(audioBuffer => {
// Extract the various channels out
let actual0 = audioBuffer.getChannelData(0);
let actual1 = audioBuffer.getChannelData(1);
let expected0 = audioBuffer.getChannelData(2);
let expected1 = audioBuffer.getChannelData(3);
// Verify that each output channel of the convolver matches
// the delayed signal from the reference
should(actual0, options.prefix + ': Channel 0')
.beCloseToArray(expected0, {absoluteThreshold: options.absoluteThresholds[0]});
should(actual1, options.prefix + ': Channel 1')
.beCloseToArray(expected1, {absoluteThreshold: options.absoluteThresholds[1]});
});
}
audit.run();
</script>
</body>
</html>

View File

@ -0,0 +1,261 @@
<!DOCTYPE html>
<html>
<head>
<title>
Test Convolver Channel Outputs for Response with 4 channels
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
// Test various convolver configurations when the convolver response has
// a four channels.
// Fairly arbitrary sample rate, except that we want the rate to be a
// power of two so that 1/sampleRate is exactly respresentable as a
// single-precision float.
let sampleRate = 8192;
// A fairly arbitrary number of frames, except the number of frames should
// be more than a few render quanta.
let renderFrames = 10 * 128;
let audit = Audit.createTaskRunner();
// Convolver response
let response;
audit.define(
{
label: 'initialize',
description: 'Convolver response with one channel'
},
(task, should) => {
// Convolver response
should(
() => {
response = new AudioBuffer(
{numberOfChannels: 4, length: 8, sampleRate: sampleRate});
// Each channel of the response is a simple impulse (with
// different delay) so that we can use a DelayNode to simulate
// the convolver output. Channel k is delayed by k+1 frames.
for (let k = 0; k < response.numberOfChannels; ++k) {
response.getChannelData(k)[k + 1] = 1;
}
},
'new AudioBuffer({numberOfChannels: 2, length: 4, sampleRate: ' +
sampleRate + '})')
.notThrow();
task.done();
});
audit.define(
{label: '1-channel input', description: 'produces 2-channel output'},
(task, should) => {
fourChannelResponseTest({numberOfInputs: 1, prefix: '1'}, should)
.then(() => task.done());
});
audit.define(
{label: '2-channel input', description: 'produces 2-channel output'},
(task, should) => {
fourChannelResponseTest({numberOfInputs: 2, prefix: '2'}, should)
.then(() => task.done());
});
audit.define(
{
label: '3-channel input',
description: '3->2 downmix producing 2-channel output'
},
(task, should) => {
fourChannelResponseTest({numberOfInputs: 3, prefix: '3'}, should)
.then(() => task.done());
});
audit.define(
{
label: '4-channel input',
description: '4->2 downmix producing 2-channel output'
},
(task, should) => {
fourChannelResponseTest({numberOfInputs: 4, prefix: '4'}, should)
.then(() => task.done());
});
audit.define(
{
label: '5.1-channel input',
description: '5.1->2 downmix producing 2-channel output'
},
(task, should) => {
fourChannelResponseTest({numberOfInputs: 6, prefix: '5.1'}, should)
.then(() => task.done());
});
audit.define(
{
label: 'delayed buffer set',
description: 'Delayed set of 4-channel response'
},
(task, should) => {
// Don't really care about the output for this test. It's to verify
// we don't crash in a debug build when setting the convolver buffer
// after creating the graph.
let context = new OfflineAudioContext(1, renderFrames, sampleRate);
let src = new OscillatorNode(context);
let convolver =
new ConvolverNode(context, {disableNormalization: true});
let buffer = new AudioBuffer({
numberOfChannels: 4,
length: 4,
sampleRate: context.sampleRate
});
// Impulse responses for the convolver aren't important, as long as
// it's not all zeroes.
for (let k = 0; k < buffer.numberOfChannels; ++k) {
buffer.getChannelData(k).fill(1);
}
src.connect(convolver).connect(context.destination);
// Set the buffer after a few render quanta have passed. The actual
// value must be least one, but is otherwise arbitrary.
context.suspend(512 / context.sampleRate)
.then(() => convolver.buffer = buffer)
.then(() => context.resume());
src.start();
context.startRendering()
.then(audioBuffer => {
// Just make sure output is not silent.
should(
audioBuffer.getChannelData(0),
'Output with delayed setting of convolver buffer')
.notBeConstantValueOf(0);
})
.then(() => task.done());
});
function fourChannelResponseTest(options, should) {
// Create an 4-channel offline context. The first two channels are for
// the stereo output of the convolver and the next two channels are for
// the reference stereo signal.
let context = new OfflineAudioContext(4, renderFrames, sampleRate);
context.destination.channelInterpretation = 'discrete';
// Create oscillators for use as the input. The type and frequency is
// arbitrary except that oscillators must be different.
let src = new Array(options.numberOfInputs);
for (let k = 0; k < src.length; ++k) {
src[k] = new OscillatorNode(
context, {type: 'square', frequency: 440 + 220 * k});
}
// Merger to combine the oscillators into one output stream.
let srcMerger =
new ChannelMergerNode(context, {numberOfInputs: src.length});
for (let k = 0; k < src.length; ++k) {
src[k].connect(srcMerger, 0, k);
}
// Convolver under test.
let conv = new ConvolverNode(
context, {disableNormalization: true, buffer: response});
srcMerger.connect(conv);
// Splitter to get individual channels of the convolver output so we can
// feed them (eventually) to the context in the right set of channels.
let splitter = new ChannelSplitterNode(context, {numberOfOutputs: 2});
conv.connect(splitter);
// Reference graph consists of a delays node to simulate the response of
// the convolver. (The convolver response is designed this way.)
let delay = new Array(4);
for (let k = 0; k < delay.length; ++k) {
delay[k] = new DelayNode(context, {
delayTime: (k + 1) / context.sampleRate,
channelCount: 1,
channelCountMode: 'explicit'
});
}
// Gain node to mix the sources to stereo in the desired way. (Could be
// done in the delay node, but let's keep the mixing separated from the
// functionality.)
let gainMixer = new GainNode(
context, {channelCount: 2, channelCountMode: 'explicit'});
srcMerger.connect(gainMixer);
// Splitter to extract the channels of the reference signal.
let refSplitter =
new ChannelSplitterNode(context, {numberOfOutputs: 2});
gainMixer.connect(refSplitter);
// Connect the left channel to the first two nodes and the right channel
// to the second two as required for "true" stereo matrix response.
for (let k = 0; k < 2; ++k) {
refSplitter.connect(delay[k], 0, 0);
refSplitter.connect(delay[k + 2], 1, 0);
}
// Gain nodes to sum the responses to stereo
let gain = new Array(2);
for (let k = 0; k < gain.length; ++k) {
gain[k] = new GainNode(context, {
channelCount: 1,
channelCountMode: 'explicit',
channelInterpretation: 'discrete'
});
}
delay[0].connect(gain[0]);
delay[2].connect(gain[0]);
delay[1].connect(gain[1]);
delay[3].connect(gain[1]);
// Final merger to bring back the individual channels from the convolver
// and the reference in the right order for the destination.
let finalMerger = new ChannelMergerNode(
context, {numberOfInputs: context.destination.channelCount});
// First two channels are for the convolver output, and the next two are
// for the reference.
splitter.connect(finalMerger, 0, 0);
splitter.connect(finalMerger, 1, 1);
gain[0].connect(finalMerger, 0, 2);
gain[1].connect(finalMerger, 0, 3);
finalMerger.connect(context.destination);
// Start the sources at last.
for (let k = 0; k < src.length; ++k) {
src[k].start();
}
return context.startRendering().then(audioBuffer => {
// Extract the various channels out
let actual0 = audioBuffer.getChannelData(0);
let actual1 = audioBuffer.getChannelData(1);
let expected0 = audioBuffer.getChannelData(2);
let expected1 = audioBuffer.getChannelData(3);
// Verify that each output channel of the convolver matches
// the delayed signal from the reference
should(actual0, options.prefix + ': Channel 0')
.beEqualToArray(expected0);
should(actual1, options.prefix + ': Channel 1')
.beEqualToArray(expected1);
});
}
audit.run();
</script>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<title>
convolver-setBuffer-null.html
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
let audit = Audit.createTaskRunner();
audit.define('test', function(task, should) {
let context = new AudioContext();
let conv = context.createConvolver();
should(() => {
conv.buffer = null;
}, 'Setting ConvolverNode impulse response buffer to null').notThrow();
should(conv.buffer === null, 'conv.buffer === null').beTrue();
task.done();
});
audit.run();
</script>
</body>
</html>