Bug 1355798 - Fix a division by zero in PannerNode.cpp. r=dminor

This happens when the listener and a PannerNode are at the same position, and
a cone gain has been specified.

The issue is that our implementation of 3d vector normalization does not
special-case vectors that have all components at zero, that dividing by zero
results in infinity, and multiplying by infinity produces NaN.

This end up setting the volume member for the output AudioChunk to NaN, and this
breaks everything downstream, of course. In practice, silence is output, with
some clicks (on linux/pulse at least).

MozReview-Commit-ID: 8u54LixvYMu

--HG--
extra : rebase_source : 3b37970b42e5c60cd6e39d3197b580edc63b5ac9
This commit is contained in:
Paul Adenot 2017-04-12 15:44:19 +02:00
parent 8cc01ae9d3
commit e34a6067fb
3 changed files with 36 additions and 0 deletions

View File

@ -36,6 +36,11 @@ struct ThreeDPoint final
void Normalize()
{
// Zero vectors cannot be normalized. For our purpose, normalizing a zero
// vector results in a zero vector.
if (IsZero()) {
return;
}
// Normalize with the maximum norm first to avoid overflow and underflow.
double invMax = 1 / MaxNorm();
x *= invMax;

View File

@ -100,6 +100,7 @@ tags=capturestream
skip-if = toolkit == 'android' # bug 1056706
[test_bug1255618.html]
[test_bug1267579.html]
[test_bug1355798.html]
[test_channelMergerNode.html]
[test_channelMergerNodeWithVolume.html]
[test_channelSplitterNode.html]

View File

@ -0,0 +1,30 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test PannerNode produces output even when the even when the distance is
from the listener is zero, and the cone gain is present, regression test for
bug 1355798.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var off = new OfflineAudioContext(1, 128, 44100);
var panner = off.createPanner();
var osc = off.createOscillator();
panner.setPosition(1, 1, 1);
off.listener.setPosition(1, 1, 1);
osc.connect(panner).connect(off.destination);
panner.coneOuterAngle = 359;
osc.start();
off.startRendering().then(function(b) {
is(b.getChannelData(0).filter(x => isNaN(x)).length, 0);
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>