summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/test/test_bug1447273.html
blob: f4b473d8abeaeee0378ed446b49a274f8213c896 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
<!DOCTYPE HTML>
<html>
<head>
  <title>Test bug 1447273 - GainNode with a stereo input and changing volume</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script type="text/javascript" src="webaudio.js"></script>
  <script type="text/javascript" src="head.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
/**
 * Sets up a stereo BufferSource and plumbs it through different gain node
 * configurations. A control gain path with no changes to gain is used along
 * with 2 other paths which should increase their gain. The result should be
 * that audio travelling along the increased gain paths is louder than the
 * control path.
 */

SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout(
  "This test uses a live audio context and uses a setTimeout to schedule a " +
  "change to the graph.");
addLoadEvent(function() {
  let context = new AudioContext();

  let numChannels = 2;
  let sampleRate = context.sampleRate;
  // 60 seconds to mitigate timing issues on slow test machines
  let recordingLength = 60;
  let bufferLength = sampleRate * recordingLength;
  let gainExplicitlyIncreased = false;
  let sourceFinished = false;

  // Create source buffer
  let sourceBuffer = context.createBuffer(numChannels, bufferLength, sampleRate);
  for (let i = 0; i < bufferLength; ++i) {
    sourceBuffer.getChannelData(0)[i] = 1;
    sourceBuffer.getChannelData(1)[i] = 1;
  }
  let source = context.createBufferSource();
  source.buffer = sourceBuffer;

  let gainNoChange = context.createGain();
  let gainExplicitAssignment = context.createGain();
  let gainSetValueAtTime = context.createGain();

  // All gain nodes start of with the same gain
  gainNoChange.gain.value = 0.25;
  gainExplicitAssignment.gain.value = 0.25;
  gainSetValueAtTime.gain.value = 0.25;

  // Connect source to gain nodes:
  // source--> gainNoChange
  //       |-> gainExplicitAssignment
  //       \-> gainSetValueAtTime
  source.connect(gainNoChange);
  source.connect(gainExplicitAssignment);
  source.connect(gainSetValueAtTime);

  // Create intermediate media streams (required to repro bug 1447273)
  let destNoChange = context.createMediaStreamDestination();
  let destExplicitAssignement = context.createMediaStreamDestination();
  let destSetValueAtTime = context.createMediaStreamDestination();

  let sourceNoChange = context.createMediaStreamSource(destNoChange.stream);
  let sourceExplicitAssignement = context.createMediaStreamSource(destExplicitAssignement.stream);
  let sourceSetValueAtTime = context.createMediaStreamSource(destSetValueAtTime.stream);

  // Connect gain nodes to our intermediate streams:
  // source--> gainNoChange           -> destNoChange            -> sourceNoChange
  //       |-> gainExplicitAssignment -> destExplicitAssignement -> sourceExplicitAssignement
  //       \-> gainSetValueAtTime     -> destSetValueAtTime      -> sourceSetValueAtTime
  gainNoChange.connect(destNoChange);
  gainExplicitAssignment.connect(destExplicitAssignement);
  gainSetValueAtTime.connect(destSetValueAtTime);

  // Create analysers for each path
  let analyserNoChange = context.createAnalyser();
  let analyserExplicitAssignment = context.createAnalyser();
  let analyserSetValueAtTime = context.createAnalyser();

  // Connect our intermediate media streams to analysers:
  // source--> gainNoChange           -> destNoChange            -> sourceNoChange            -> analyserNoChange
  //       |-> gainExplicitAssignment -> destExplicitAssignement -> sourceExplicitAssignement -> analyserExplicitAssignment
  //       \-> gainSetValueAtTime     -> destSetValueAtTime      -> sourceSetValueAtTime      -> analyserSetValueAtTime
  sourceNoChange.connect(analyserNoChange);
  sourceExplicitAssignement.connect(analyserExplicitAssignment);
  sourceSetValueAtTime.connect(analyserSetValueAtTime);

  // Two seconds in, increase gain for setValueAt path
  gainSetValueAtTime.gain.setValueAtTime(0.5, 2);

  // Maximum values seen at each analyser node, will be updated by
  // checkAnalysersForMaxValues() during test.
  let maxNoGainChange = 0;
  let maxExplicitAssignment = 0;
  let maxSetValueAtTime = 0;

  // Poll analysers and check for max values
  function checkAnalysersForMaxValues() {
    let findMaxValue =
      (array) => array.reduce((a, b) => Math.max(Math.abs(a), Math.abs(b)));

    let dataArray = new Float32Array(analyserNoChange.fftSize);
    analyserNoChange.getFloatTimeDomainData(dataArray);
    maxNoGainChange = Math.max(maxNoGainChange, findMaxValue(dataArray));

    analyserExplicitAssignment.getFloatTimeDomainData(dataArray);
    maxExplicitAssignment = Math.max(maxExplicitAssignment, findMaxValue(dataArray));

    analyserSetValueAtTime.getFloatTimeDomainData(dataArray);
    maxSetValueAtTime = Math.max(maxSetValueAtTime, findMaxValue(dataArray));

    // End test if we've met our conditions
    // Add a small amount to initial gain to make sure we're not getting
    // passes due to rounding errors.
    let epsilon = 0.01;
    if (maxExplicitAssignment > (maxNoGainChange + epsilon) &&
        maxSetValueAtTime > (maxNoGainChange + epsilon)) {
      source.stop();
    }
  }

  source.onended = () => {
    sourceFinished = true;
    info(`maxNoGainChange: ${maxNoGainChange}`);
    info(`maxExplicitAssignment: ${maxExplicitAssignment}`);
    info(`maxSetValueAtTime: ${maxSetValueAtTime}`);
    ok(gainExplicitlyIncreased,
       "Gain should be explicitly assinged during test!");
    // Add a small amount to initial gain to make sure we're not getting
    // passes due to rounding errors.
    let epsilon = 0.01;
    ok(maxExplicitAssignment > (maxNoGainChange + epsilon),
       "Volume should increase due to explicit assignment to gain.value");
    ok(maxSetValueAtTime > (maxNoGainChange + epsilon),
       "Volume should increase due to setValueAtTime on gain.value");
    SimpleTest.finish();
  };

  // Start the graph
  source.start(0);

  // We'll use this callback to check our analysers for gain
  function animationFrameCb() {
    if (sourceFinished) {
      return;
    }
    requestAnimationFrame(animationFrameCb);
    checkAnalysersForMaxValues();
  }

  // Using timers is gross, but as of writing there doesn't appear to be a
  // nicer way to perform gain.value = 0.5 on our node. When/if we support
  // suspend(time) on offline contexts we could potentially use that instead.

  // Roughly 2 seconds through our source buffer (setTimeout flakiness) increase
  // our gain on gainExplicitAssignment path.
  window.setTimeout(() => {
    gainExplicitAssignment.gain.value = 0.5;
    // Make debugging flaky timeouts in test easier
    info("Gain explicitly set!")
    gainExplicitlyIncreased = true;
    // Start checking analysers, we do this only after changing volume to avoid
    // possible starvation of this timeout from requestAnimationFrame calls.
    animationFrameCb();
  }, 2000);
});

</script>
</pre>
</body>
</html>