Commit 9007ef13 authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

Add preservesPitch tests

This CL adds Web Platform Tests that check the behavior of the
preservesPitch flag.

The tests rely on WebAudio to analyze the frequency of an Audio element,
as the playback rate changes.

Firefox's mozPreservePitch does not affect MediaElementAudioSourceNode,
which means that these tests won't work on Firefox. See:
https://bugzilla.mozilla.org/show_bug.cgi?id=1648277

Change-Id: Ic25d474f96e32eb8d8584188d879a018daa6ae73
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2259375
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: default avatarPhilip Jägenstedt <foolip@chromium.org>
Cr-Commit-Position: refs/heads/master@{#787947}
parent 9ee4a145
......@@ -36,6 +36,8 @@ t('initialTime'); // added in r5310, removed in r7046.
t('audio', 'video'); // added in r5636, replaced with muted in r5991.
t('startDate'); // added in r7045, replaced with getStartDate() in r8113.
t('mozSrcObject'); // never in the spec
t('mozPreservesPitch'); // prefixed version should be removed.
t('webkitPreservesPitch'); // prefixed version should be removed.
// TextTrackCue constructor: added in r5723, removed in r7742.
test(function() {
......
// This should be removed when the webaudio/historical.html tests are passing.
// Tracking bug: https://bugs.webkit.org/show_bug.cgi?id=204719
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var FFT_SIZE = 2048;
function getPitchDetector(media, t) {
var audioContext = new AudioContext();
t.add_cleanup(() => audioContext.close());
var sourceNode = audioContext.createMediaElementSource(media);
var analyser = audioContext.createAnalyser();
analyser.fftSize = FFT_SIZE;
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);
}
function getPitch(analyser, binConverter) {
var buf = new Uint8Array(FFT_SIZE/2);
analyser.getByteFrequencyData(buf);
return findDominantFrequency(buf, binConverter);
}
// Returns the dominant frequency, +/- a certain margin.
function findDominantFrequency(buf, binConverter) {
var max = 0;
var bin = 0;
for (var i=0;i<buf.length;i++) {
if(buf[i] > max) {
max = buf[i];
bin = i;
}
}
// 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.
return { value:binConverter(bin), margin:binConverter(1) };
}
\ No newline at end of file
<!DOCTYPE html>
<title>Test preservesPitch.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="pitch-detector.js"></script>
<script>
// Remove when media-elements/historical.html's preservePitch prefix tests are are passing.
function getPreservesPitch(audio) {
if ("preservesPitch" in HTMLAudioElement.prototype) {
return audio.preservesPitch;
}
if ("mozPreservesPitch" in HTMLAudioElement.prototype) {
return audio.mozPreservesPitch;
}
if ("wekbitPreservesPitch" in HTMLAudioElement.prototype) {
return audio.wekbitPreservesPitch;
}
return undefined;
}
// Remove when media-elements/historical.html's preservePitch prefix tests are are passing.
function setPreservesPitch(audio, value) {
if ("preservesPitch" in HTMLAudioElement.prototype) {
audio.preservesPitch = value;
} else if ("mozPreservesPitch" in HTMLAudioElement.prototype) {
audio.mozPreservesPitch = value;
} else if ("wekbitPreservesPitch" in HTMLAudioElement.prototype) {
audio.wekbitPreservesPitch = value;
}
}
test(function(t) {
assert_true("preservesPitch" in HTMLAudioElement.prototype);
}, "Test that preservesPitch is present and unprefixed.");
test(function(t) {
let audio = document.createElement('audio');
assert_true(getPreservesPitch(audio));
setPreservesPitch(audio, false);
assert_false(getPreservesPitch(audio));
}, "Test that presevesPitch is on by default");
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";
audio.playbackRate = playbackRate;
setPreservesPitch(audio, preservesPitch);
function promiseTimeUpdate() {
return new Promise((resolve) => audio.ontimeupdate = resolve);
}
function verifyPitch() {
var pitch = detector();
// 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;
testPreservesPitch(true, 1.0, REFERENCE_PITCH,
"The default playbackRate should not affect pitch")
testPreservesPitch(false, 1.0, REFERENCE_PITCH,
"The default playbackRate should not affect pitch, even with preservesPitch=false")
testPreservesPitch(true, 2.0, REFERENCE_PITCH,
"Speed-ups should not change the pitch when preservesPitch=true")
testPreservesPitch(true, 0.5, REFERENCE_PITCH,
"Slow-downs should not change the pitch when preservesPitch=true")
testPreservesPitch(false, 2.0, REFERENCE_PITCH*2.0,
"Speed-ups should change the pitch when preservesPitch=false")
testPreservesPitch(false, 0.5, REFERENCE_PITCH*0.5,
"Slow-downs should change the pitch when preservesPitch=false")
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment