0

Reland preservesPitch test deflake, fix timeouts

This CL relands "Deflake preservesPitch tests", which was reverted by:
commit f19f340437.

It also attempts to incorporate some of the WPT changes made upstream,
but not yet landed, in:
https://github.com/web-platform-tests/wpt/pull/24599

Specifically, it fixes typos in the Safari prefixes, and explicitly
starts the audio context. However, it is different from the PR, since
it reuses the same Audio element, MediaElementAudioSourceNode and
AudioContext. This is an attempt to cut down on overhead costs and
fix test timeouts. We instead reset the audio.currentTime to 0, and
only create a new analyser node for each test.

Bug: 1105877, 1096238
Change-Id: Ie528ec0b7c38d9df59fcb04696c810e6d1c232f6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2300929
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Auto-Submit: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: Philip Jägenstedt <foolip@chromium.org>
Cr-Commit-Position: refs/heads/master@{#789262}
This commit is contained in:
Thomas Guilbert
2020-07-16 23:50:31 +00:00
committed by Commit Bot
parent 16a881118b
commit 2cbc3cc465
2 changed files with 86 additions and 45 deletions
third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements

@@ -4,11 +4,14 @@ window.AudioContext = window.AudioContext || window.webkitAudioContext;
var FFT_SIZE = 2048;
function getPitchDetector(media, t) {
var audioContext = new AudioContext();
t.add_cleanup(() => audioContext.close());
var audioContext;
var sourceNode;
var sourceNode = audioContext.createMediaElementSource(media);
function getPitchDetector(media) {
if(!audioContext) {
audioContext = new AudioContext();
sourceNode = audioContext.createMediaElementSource(media);
}
var analyser = audioContext.createAnalyser();
analyser.fftSize = FFT_SIZE;
@@ -16,14 +19,22 @@ function getPitchDetector(media, t) {
sourceNode.connect(analyser);
analyser.connect(audioContext.destination);
// Returns the frequency value for the nth FFT bin.
var binConverter = (bin) => audioContext.sampleRate*(bin/FFT_SIZE);
return () => getPitch(analyser, binConverter);
return {
ensureStart() { return audioContext.resume(); },
detect() { return getPitch(analyser); },
cleanup() {
sourceNode.disconnect();
analyser.disconnect();
},
};
}
function getPitch(analyser, binConverter) {
var buf = new Uint8Array(FFT_SIZE/2);
function getPitch(analyser) {
// Returns the frequency value for the nth FFT bin.
var binConverter = (bin) =>
(audioContext.sampleRate/2)*((bin)/(analyser.frequencyBinCount-1));
var buf = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(buf);
return findDominantFrequency(buf, binConverter);
}
@@ -40,9 +51,8 @@ function findDominantFrequency(buf, binConverter) {
}
}
// The distance between bins is always constant and corresponds to
// (1/FFT_SIZE)th of the sample rate. Use the frequency value of the 1st bin
// as the margin directly, instead of calculating an average from the values
// of the neighboring bins.
// The spread of frequencies within bins is constant and corresponds to
// (1/(FFT_SIZE-1))th of the sample rate. Use the value of bin #1 as a
// shorthand for that value.
return { value:binConverter(bin), margin:binConverter(1) };
}

@@ -15,8 +15,8 @@ function getPreservesPitch(audio) {
if ("mozPreservesPitch" in HTMLAudioElement.prototype) {
return audio.mozPreservesPitch;
}
if ("wekbitPreservesPitch" in HTMLAudioElement.prototype) {
return audio.wekbitPreservesPitch;
if ("webkitPreservesPitch" in HTMLAudioElement.prototype) {
return audio.webkitPreservesPitch;
}
return undefined;
}
@@ -27,8 +27,8 @@ function setPreservesPitch(audio, value) {
audio.preservesPitch = value;
} else if ("mozPreservesPitch" in HTMLAudioElement.prototype) {
audio.mozPreservesPitch = value;
} else if ("wekbitPreservesPitch" in HTMLAudioElement.prototype) {
audio.wekbitPreservesPitch = value;
} else if ("webkitPreservesPitch" in HTMLAudioElement.prototype) {
audio.webkitPreservesPitch = value;
}
}
@@ -37,51 +37,82 @@ test(function(t) {
}, "Test that preservesPitch is present and unprefixed.");
test(function(t) {
let audio = document.createElement('audio');
assert_true(getPreservesPitch(audio));
let defaultAudio = document.createElement('audio');
assert_true(getPreservesPitch(defaultAudio));
setPreservesPitch(audio, false);
assert_false(getPreservesPitch(audio));
}, "Test that presevesPitch is on by default");
setPreservesPitch(defaultAudio, false);
assert_false(getPreservesPitch(defaultAudio));
}, "Test that preservesPitch is on by default");
var audio;
function addTestCleanups(t, detector) {
t.add_cleanup(() => {
audio.pause();
audio.currentTime = 0;
});
t.add_cleanup(() => detector.cleanup());
}
function testPreservesPitch(preservesPitch, playbackRate, expectedPitch, description) {
promise_test(async t => {
let audio = document.createElement('audio');
var detector = getPitchDetector(audio, t);
// This file contains 5 seconds of a 440hz sine wave.
audio.src = "/media/sine440.mp3";
let detector = getPitchDetector(audio);
addTestCleanups(t, detector);
audio.playbackRate = playbackRate;
setPreservesPitch(audio, preservesPitch);
function promiseTimeUpdate() {
return new Promise((resolve) => audio.ontimeupdate = resolve);
function waitUntil(time) {
return new Promise((resolve) => {
audio.ontimeupdate = () => {
if (audio.currentTime >= time) {
resolve();
}
};
});
}
function verifyPitch() {
var pitch = detector();
// Wait until we have played some audio. Otherwise, the detector
// might return a pitch of 0Hz.
audio.play();
await waitUntil(0.25);
// 25Hz is larger than the margin we get from 48kHz and 44.1kHz
// audio being analyzed by a FFT of size 2048. If we get something
// different, there is an error within the test's calculations (or
// we might be dealing a larger sample rate).
assert_less_than(pitch.margin, 25,
"Test error: the margin should be reasonably small.")
var pitch = detector.detect();
assert_approx_equals(pitch.value, expectedPitch, pitch.margin,
"The actual pitch should be close to the expected pitch.");
}
// 25Hz is larger than the margin we get from 48kHz and 44.1kHz
// audio being analyzed by a FFT of size 2048. If we get something
// different, there is an error within the test's calculations (or
// we might be dealing a larger sample rate).
assert_less_than(pitch.margin, 25,
"Test error: the margin should be reasonably small.")
assert_approx_equals(pitch.value, expectedPitch, pitch.margin,
"The actual pitch should be close to the expected pitch.");
await test_driver.bless("Play audio element", () => audio.play() )
.then(promiseTimeUpdate)
.then(verifyPitch);
}, description);
}
var REFERENCE_PITCH = 440;
promise_test(async t => {
// Create the audio element only once, in order to lower the chances of
// tests timing out.
audio = document.createElement('audio');
// This file contains 5 seconds of a 440hz sine wave.
audio.src = "/media/sine440.mp3";
let detector = getPitchDetector(audio);
addTestCleanups(t, detector);
// The first time we run the test, we need to interact with the
// AudioContext and Audio element via user gestures.
await test_driver.bless("Play audio element", () => {
return Promise.all([audio.play(), detector.ensureStart()]);
});
}, "Setup Audio element and AudioContext")
testPreservesPitch(true, 1.0, REFERENCE_PITCH,
"The default playbackRate should not affect pitch")