зеркало из https://github.com/mozilla/gecko-dev.git
bug 1474222 test ConvolverNode up-mixing r=padenot
MozReview-Commit-ID: GZVe85depAj --HG-- extra : rebase_source : e65cea213bc8582c54ecfe9f995928b4c05a443c
This commit is contained in:
Родитель
7ee8880ce6
Коммит
09c58a5f32
|
@ -123,6 +123,7 @@ skip-if = (android_version == '18' && debug) # bug 1158417
|
||||||
[test_convolverNodeNormalization.html]
|
[test_convolverNodeNormalization.html]
|
||||||
[test_convolverNodePassThrough.html]
|
[test_convolverNodePassThrough.html]
|
||||||
[test_convolverNodeWithGain.html]
|
[test_convolverNodeWithGain.html]
|
||||||
|
[test_convolver-upmixing-1-channel-response.html]
|
||||||
[test_currentTime.html]
|
[test_currentTime.html]
|
||||||
[test_decodeAudioDataOnDetachedBuffer.html]
|
[test_decodeAudioDataOnDetachedBuffer.html]
|
||||||
[test_decodeAudioDataPromise.html]
|
[test_decodeAudioDataPromise.html]
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>Test that up-mixing signals in ConvolverNode processing is linear</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script>
|
||||||
|
const EPSILON = 3.0 * Math.pow(2, -22);
|
||||||
|
// sampleRate is a power of two so that delay times are exact in base-2
|
||||||
|
// floating point arithmetic.
|
||||||
|
const SAMPLE_RATE = 32768;
|
||||||
|
// Length of stereo convolver input in frames (arbitrary):
|
||||||
|
const STEREO_FRAMES = 256;
|
||||||
|
// Length of mono signal in frames. This is more than two blocks to ensure
|
||||||
|
// that at least one block will be mono, even if interpolation in the
|
||||||
|
// DelayNode means that stereo is output one block earlier and later than
|
||||||
|
// if frames are delayed without interpolation.
|
||||||
|
const MONO_FRAMES = 384;
|
||||||
|
// Length of response buffer:
|
||||||
|
const RESPONSE_FRAMES = 256;
|
||||||
|
|
||||||
|
function test_linear_upmixing(channelInterpretation, initial_mono_frames)
|
||||||
|
{
|
||||||
|
let stereo_input_end = initial_mono_frames + STEREO_FRAMES;
|
||||||
|
// Total length:
|
||||||
|
let length = stereo_input_end + RESPONSE_FRAMES + MONO_FRAMES + STEREO_FRAMES;
|
||||||
|
// The first two channels contain signal where some up-mixing occurs
|
||||||
|
// internally to a ConvolverNode when a stereo signal is added and removed.
|
||||||
|
// The last two channels are expected to contain the same signal, but mono
|
||||||
|
// and stereo signals are convolved independently before up-mixing the mono
|
||||||
|
// output to mix with the stereo output.
|
||||||
|
let context = new OfflineAudioContext({numberOfChannels: 4,
|
||||||
|
length: length,
|
||||||
|
sampleRate: SAMPLE_RATE});
|
||||||
|
|
||||||
|
let response = new AudioBuffer({numberOfChannels: 1,
|
||||||
|
length: RESPONSE_FRAMES,
|
||||||
|
sampleRate: context.sampleRate});
|
||||||
|
|
||||||
|
// Two stereo channel splitters will collect test and reference outputs.
|
||||||
|
let destinationMerger = new ChannelMergerNode(context, {numberOfInputs: 4});
|
||||||
|
destinationMerger.connect(context.destination);
|
||||||
|
let testSplitter =
|
||||||
|
new ChannelSplitterNode(context, {numberOfOutputs: 2});
|
||||||
|
let referenceSplitter =
|
||||||
|
new ChannelSplitterNode(context, {numberOfOutputs: 2});
|
||||||
|
testSplitter.connect(destinationMerger, 0, 0);
|
||||||
|
testSplitter.connect(destinationMerger, 1, 1);
|
||||||
|
referenceSplitter.connect(destinationMerger, 0, 2);
|
||||||
|
referenceSplitter.connect(destinationMerger, 1, 3);
|
||||||
|
|
||||||
|
// A GainNode mixes reference stereo and mono signals because up-mixing
|
||||||
|
// cannot be performed at a channel splitter.
|
||||||
|
let referenceGain = new GainNode(context);
|
||||||
|
referenceGain.connect(referenceSplitter);
|
||||||
|
referenceGain.channelInterpretation = channelInterpretation;
|
||||||
|
|
||||||
|
// The impulse response for convolution contains two impulses so as to test
|
||||||
|
// effects in at least two processing blocks.
|
||||||
|
response.getChannelData(0)[0] = 0.5;
|
||||||
|
response.getChannelData(0)[response.length - 1] = 0.5;
|
||||||
|
|
||||||
|
let testConvolver = new ConvolverNode(context, {disableNormalization: true,
|
||||||
|
buffer: response});
|
||||||
|
testConvolver.channelInterpretation = channelInterpretation;
|
||||||
|
let referenceMonoConvolver = new ConvolverNode(context,
|
||||||
|
{disableNormalization: true,
|
||||||
|
buffer: response});
|
||||||
|
let referenceStereoConvolver = new ConvolverNode(context,
|
||||||
|
{disableNormalization: true,
|
||||||
|
buffer: response});
|
||||||
|
// No need to set referenceStereoConvolver.channelInterpretation because
|
||||||
|
// input is either silent or stereo.
|
||||||
|
testConvolver.connect(testSplitter);
|
||||||
|
// Mix reference convolver output.
|
||||||
|
referenceMonoConvolver.connect(referenceGain);
|
||||||
|
referenceStereoConvolver.connect(referenceGain);
|
||||||
|
|
||||||
|
// The DelayNode initially has a single channel of silence, which is used to
|
||||||
|
// switch the stereo signal in and out. The output of the delay node is
|
||||||
|
// first mono silence (if there is a non-zero initial_mono_frames), then
|
||||||
|
// stereo, then mono silence, and finally stereo again. maxDelayTime is
|
||||||
|
// used to generate the middle mono silence period from the initial silence
|
||||||
|
// in the DelayNode and then generate the final period of stereo from its
|
||||||
|
// initial input.
|
||||||
|
let maxDelayTime = (length - STEREO_FRAMES) / context.sampleRate;
|
||||||
|
let delay =
|
||||||
|
new DelayNode(context,
|
||||||
|
{maxDelayTime: maxDelayTime,
|
||||||
|
delayTime: initial_mono_frames / context.sampleRate});
|
||||||
|
// Schedule an increase in the delay to return to mono silence.
|
||||||
|
delay.delayTime.setValueAtTime(maxDelayTime,
|
||||||
|
stereo_input_end / context.sampleRate);
|
||||||
|
delay.connect(testConvolver);
|
||||||
|
delay.connect(referenceStereoConvolver);
|
||||||
|
|
||||||
|
let stereoMerger = new ChannelMergerNode(context, {numberOfInputs: 2});
|
||||||
|
stereoMerger.connect(delay);
|
||||||
|
|
||||||
|
// Three independent signals
|
||||||
|
let monoSignal = new OscillatorNode(context, {frequency: 440});
|
||||||
|
let leftSignal = new OscillatorNode(context, {frequency: 450});
|
||||||
|
let rightSignal = new OscillatorNode(context, {frequency: 460});
|
||||||
|
monoSignal.connect(testConvolver);
|
||||||
|
monoSignal.connect(referenceMonoConvolver);
|
||||||
|
leftSignal.connect(stereoMerger, 0, 0);
|
||||||
|
rightSignal.connect(stereoMerger, 0, 1);
|
||||||
|
monoSignal.start();
|
||||||
|
leftSignal.start();
|
||||||
|
rightSignal.start();
|
||||||
|
|
||||||
|
return context.startRendering().
|
||||||
|
then((buffer) => {
|
||||||
|
let maxDiff = -1.0;
|
||||||
|
let frameIndex = 0;
|
||||||
|
let channelIndex = 0;
|
||||||
|
for (let c = 0; c < 2; ++c) {
|
||||||
|
let testOutput = buffer.getChannelData(0 + c);
|
||||||
|
let referenceOutput = buffer.getChannelData(2 + c);
|
||||||
|
for (var i = 0; i < buffer.length; ++i) {
|
||||||
|
var diff = Math.abs(testOutput[i] - referenceOutput[i]);
|
||||||
|
if (diff > maxDiff) {
|
||||||
|
maxDiff = diff;
|
||||||
|
frameIndex = i;
|
||||||
|
channelIndex = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_approx_equals(buffer.getChannelData(0 + channelIndex)[frameIndex],
|
||||||
|
buffer.getChannelData(2 + channelIndex)[frameIndex],
|
||||||
|
EPSILON,
|
||||||
|
`output at ${frameIndex} ` +
|
||||||
|
`in channel ${channelIndex}` );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
promise_test(() => test_linear_upmixing("speakers", MONO_FRAMES),
|
||||||
|
"speakers, initially mono");
|
||||||
|
promise_test(() => test_linear_upmixing("discrete", MONO_FRAMES),
|
||||||
|
"discrete");
|
||||||
|
// Gecko uses a separate path for "speakers" up-mixing when the convolver's
|
||||||
|
// first input is stereo, so test that separately.
|
||||||
|
promise_test(() => test_linear_upmixing("speakers", 0),
|
||||||
|
"speakers, initially stereo");
|
||||||
|
</script>
|
Загрузка…
Ссылка в новой задаче