summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/k-rate-panner.html
blob: 60200b24712c29a92d73b6215b85f734999b8209 (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
176
177
178
<!doctype html>
<html>
  <head>
    <title>Test k-rate AudioParams of PannerNode</title>
    <script src="/resources/testharness.js"></script>
    <script src="/resources/testharnessreport.js"></script>
    <script src="/webaudio/resources/audit-util.js"></script>
    <script src="/webaudio/resources/audit.js"></script>
    <script src="automation-rate-testing.js"></script>
  </head>

  <body>
    <script>
      let audit = Audit.createTaskRunner();

      // Define a test where we verify that a k-rate audio param produces
      // different results from an a-rate audio param for each of the audio
      // params of a biquad.
      //
      // Each entry gives the name of the AudioParam, an initial value to be
      // used with setValueAtTime, and a final value to be used with
      // linearRampToValueAtTime. (See |doTest| for details as well.)

      [{name: 'positionX', initial: 0, final: 1000},
       {name: 'positionY', initial: 0, final: 1000},
       {name: 'orientationX', initial: 1, final: 10},
       {name: 'orientationY', initial: 1, final: 10},
       {name: 'orientationZ', initial: 1, final: 10},
      ].forEach(paramProperty => {
        audit.define('Panner k-rate ' + paramProperty.name, (task, should) => {
          // Arbitrary sample rate and duration.
          let sampleRate = 8000;
          let testDuration = 5 * 128 / sampleRate;
          let context = new OfflineAudioContext({
            numberOfChannels: 3,
            sampleRate: sampleRate,
            length: testDuration * sampleRate
          });

          doTest(context, should, {
            sourceNodeName: 'ConstantSourceNode',
            verifyPieceWiseConstant: true,
            nodeName: 'PannerNode',
            // Make the source directional so orientation matters, and set some
            // defaults for the position and orientation so that we're not on an
            // axis where the azimuth and elevation might be constant when
            // moving one of the AudioParams.
            nodeOptions: {
              distanceModel: 'inverse',
              coneOuterAngle: 360,
              coneInnerAngle: 0,
              positionX: 1,
              positionY: 1,
              positionZ: 1,
              orientationX: 0,
              orientationY: 1,
              orientationZ: 1
            },
            prefix: `k-rate ${paramProperty.name}`,
            // Just set the frequency to k-rate
            rateSettings: [
              {name: paramProperty.name, value: 'k-rate'},
            ],
            // Automate just the given AudioParam
            automations: [{
              name: paramProperty.name,
              methods: [
                {name: 'setValueAtTime', options: [paramProperty.initial, 0]}, {
                  name: 'linearRampToValueAtTime',
                  options: [paramProperty.final, testDuration]
                }
              ]
            }]
          }).then(() => task.done());
        });
      });

      // Test k-rate automation of the listener.  The intial and final
      // automation values are pretty arbitrary, except that they should be such
      // that the panner and listener produces non-constant output.
      [{name: 'positionX', initial: [1, 0], final: [1000, 1]},
       {name: 'positionY', initial: [1, 0], final: [1000, 1]},
       {name: 'positionZ', initial: [1, 0], final: [1000, 1]},
       {name: 'forwardX', initial: [-1, 0], final: [1, 1]},
       {name: 'forwardY', initial: [-1, 0], final: [1, 1]},
       {name: 'forwardZ', initial: [-1, 0], final: [1, 1]},
       {name: 'upX', initial: [-1, 0], final: [1000, 1]},
       {name: 'upY', initial: [-1, 0], final: [1000, 1]},
       {name: 'upZ', initial: [-1, 0], final: [1000, 1]},
      ].forEach(paramProperty => {
        audit.define(
            'Listener k-rate ' + paramProperty.name, (task, should) => {
              // Arbitrary sample rate and duration.
              let sampleRate = 8000;
              let testDuration = 5 * 128 / sampleRate;
              let context = new OfflineAudioContext({
                numberOfChannels: 1,
                sampleRate: sampleRate,
                length: testDuration * sampleRate
              });

              doListenerTest(context, should, {
                param: paramProperty.name,
                initial: paramProperty.initial,
                final: paramProperty.final
              }).then(() => task.done());
            });
      });

      audit.run();

      function doListenerTest(context, should, options) {
        let src = new ConstantSourceNode(context);
        let panner = new PannerNode(context, {
          distanceModel: 'inverse',
          coneOuterAngle: 360,
          coneInnerAngle: 10,
          positionX: 10,
          positionY: 10,
          positionZ: 10,
          orientationX: 1,
          orientationY: 1,
          orientationZ: 1
        });

        src.connect(panner).connect(context.destination);

        src.start();

        let listener = context.listener;

        // Set listener properties to "random" values so that motion on one of
        // the attributes actually changes things relative to the panner
        // location.  And the up and forward directions should have a simple
        // relationship between them.
        listener.positionX.value = -1;
        listener.positionY.value = 1;
        listener.positionZ.value = -1;
        listener.forwardX.value = -1;
        listener.forwardY.value = 1;
        listener.forwardZ.value = -1;
        // Make the up vector not parallel or perpendicular to the forward and
        // position vectors so that automations of the up vector produce
        // noticeable differences.
        listener.upX.value = 1;
        listener.upY.value = 1;
        listener.upZ.value = 2;

        let audioParam = listener[options.param];
        audioParam.automationRate = 'k-rate';

        let prefix = `Listener ${options.param}`;
        should(audioParam.automationRate, prefix + '.automationRate')
            .beEqualTo('k-rate');
        should(() => {
          audioParam.setValueAtTime(...options.initial);
        }, prefix + `.setValueAtTime(${options.initial})`).notThrow();
        should(() => {
          audioParam.linearRampToValueAtTime(...options.final);
        }, prefix + `.linearRampToValueAtTime(${options.final})`).notThrow();

        return context.startRendering().then(renderedBuffer => {
          let prefix = `Listener k-rate ${options.param}: `;
          let output = renderedBuffer.getChannelData(0);
          // Sanity check that the output isn't constant.
          should(output, prefix + `Output`).notBeConstantValueOf(output[0]);

          // Verify that the output is constant over each render quantum
          for (let k = 0; k < output.length; k += 128) {
            should(
                output.slice(k, k + 128), prefix + `Output [${k}, ${k + 127}]`)
                .beConstantValueOf(output[k]);
          }
        });
      }
    </script>
  </body>
</html>