summaryrefslogtreecommitdiffstats
path: root/third_party/WebKit/LayoutTests/webaudio/audiobuffer-resample.html
blob: 0a56884cc427f8ea63ad2eb743d3078fcedec6ce (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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <script src="resources/compatibility.js"></script>
    <script src="resources/audio-testing.js"></script>
    <script src="../resources/js-test.js"></script>
  </head>

  <body>
    <div id="description"></div>
    <div id="console"></div>

    <script>
      // These are global to make debugging a little easier.
      var context;
      var buffer;
      var source;
      var renderedData;
      var trueData;
      var signalEnergy;
      var noiseEnergy;
      var maxError;

      // Context sample rate.
      var sampleRate = 48000;
      // The sample rate of the buffer.
      var bufferRate = 3000;
      // The audio buffer is a sine wave of this frequency.
      var toneFrequency = 440;
      // How long test is
      var lengthInSeconds = 0.5;
      // The maximum allowed peak error between the actual and true output. This value was
      // experimentally determined for the given bufferRate.
      var peakThreshold = 0.11;
      // The minimum SNR allowed between the actual and true output.
      var snrThreshold = 22.35;

      description("Test resampling of an AudioBuffer at " + bufferRate + " Hz");

      function log10(x) {
          return Math.log(x)/Math.LN10;
      }
      
      // Generate a sine wave in an AudioBuffer using the given |freq|. The AudioBuffer has the
      // sample rate of |rate|.
      function createSineBuffer(context, freq, rate) {
          var buf = context.createBuffer(1, lengthInSeconds * rate, rate);
          var omega = 2 * Math.PI * freq / rate;
          var signal = buf.getChannelData(0);
          var length = signal.length;
          for (var k = 0; k < length; ++k)
            signal[k] = Math.sin(omega * k);

          return buf;
      }

      // Check the output against the expected output.
      function checkResult(event) {
          renderedData = event.renderedBuffer.getChannelData(0);
          var length = renderedData.length;
          // Generate a reference sine wave at the context rate
          var trueReference = createSineBuffer(context, toneFrequency, context.sampleRate);
          trueData = trueReference.getChannelData(0);

          // To compare the actual output against the reference, we compute the peak error and the
          // SNR between the two.
          signalEnergy = 0;
          noiseEnergy = 0;
          maxError = -1;

          var success = true;

          // Compute the peak error and the SNR.
          for (var k = 0; k < length / 2; ++k) {
              var diff = renderedData[k] - trueData[k];
              noiseEnergy += diff * diff;
              signalEnergy += trueData[k] * trueData[k];
              if (Math.abs(diff) > maxError)
                  maxError = Math.abs(diff);
          }

          var snr;

          if (noiseEnergy == 0)
              snr = 1000;
          else
              snr = 10 * log10(signalEnergy / noiseEnergy);
      
          if (maxError < peakThreshold) {
              testPassed("Peak error between actual and reference data below threshold of " +
                         peakThreshold + ".");
          } else {
              testFailed("Peak error of " + maxError + " exceeds threshold of " +
                         peakThreshold + ".");
              success = false;
          }

          if (snr > snrThreshold) {
              testPassed("SNR exceeds threshold of " + snrThreshold + " dB.");
          } else {
              testFailed("SNR of " + snr + " is below the threshold of " + snrThreshold + ".");
              success = false;
          }
      
          if (success)
              testPassed("AudioBuffer resampling is accurate for buffer rate of " +
                         bufferRate + " Hz.");
          else
              testFailed("AudioBuffer resampling is not accurate enough for buffer rate of " +
                         bufferRate + " Hz.");

          finishJSTest();
      }

      function runTest() {
          if (window.testRunner) {
              testRunner.dumpAsText();
              testRunner.waitUntilDone();
          }

          window.jsTestIsAsync = true;

          context = new OfflineAudioContext(1, lengthInSeconds * sampleRate, sampleRate);

          // Create a sine wave in a buffer that's different from the context rate to test
          // resampling.
          buffer = createSineBuffer(context, toneFrequency, bufferRate);
          source = context.createBufferSource();
          source.buffer = buffer;
          source.connect(context.destination);
          source.start();

          context.oncomplete = checkResult;
          context.startRendering();
      }
      runTest();
      successfullyParsed = true;
    </script>
  </body>
</html>