summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/embedded-content/media-elements/preserves-pitch.html
blob: ba76f51d47192f3248fe1f618e85f85ccc7cb0bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<!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 ("webkitPreservesPitch" in HTMLAudioElement.prototype) {
        return audio.webkitPreservesPitch;
    }
    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 ("webkitPreservesPitch" in HTMLAudioElement.prototype) {
        audio.webkitPreservesPitch = value;
    }
}

test(function(t) {
    assert_true("preservesPitch" in HTMLAudioElement.prototype);
}, "Test that preservesPitch is present and unprefixed.");

test(function(t) {
    let defaultAudio = document.createElement('audio');
    assert_true(getPreservesPitch(defaultAudio));

    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 detector = getPitchDetector(audio);
        addTestCleanups(t, detector);

        audio.playbackRate = playbackRate;
        setPreservesPitch(audio, preservesPitch);

        function waitUntil(time) {
            return new Promise((resolve) => {
                audio.ontimeupdate = () => {
                    if (audio.currentTime >= time) {
                        resolve();
                    }
                };
            });
        }

        // Wait until we have played some audio. Otherwise, the detector
        // might return a pitch of 0Hz.
        audio.play();
        await waitUntil(0.5);

        var pitch = detector.detect();

        // 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.")

        // Allow for a 15% margin of error in the pitch detector, to reduce test
        // flakiness. Since our tests speed up and slow down by a factor of 2,
        // this should be plenty of leeway, without causing false negatives.
        assert_approx_equals(pitch.value, expectedPitch, expectedPitch*0.15,
            "The actual pitch should be close to the expected pitch.");

    }, 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")

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>