summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/k-rate-audiobuffersource-connections.html
blob: 0b94bd70f9f7c5da26654845ec784d6805f4117e (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
<!doctype html>
<html>
  <head>
    <title>k-rate AudioParams with inputs for AudioBufferSourceNode</title>
    <script src="/resources/testharness.js"></script>
    <script src="/resources/testharnessreport.js"></script>
    <script src="/webaudio/resources/audit.js"></script>
    <script src="/webaudio/resources/audit-util.js"></script>
  </head>

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

      // Fairly abitrary sampleRate and somewhat duration
      const sampleRate = 8000;
      const testDuration = 0.25;

      [['playbackRate', [1, 0], [2, testDuration]],
       ['detune', [-1200, 0], [1200, testDuration]]]
          .forEach(param => {
            audit.define(
                {label: param[0], description: `AudioBufferSource ${param[0]}`},
                async (task, should) => {
                  await doTest(should, {
                    prefix: task.label,
                    paramName: param[0],
                    startValue: param[1],
                    endValue: param[2]
                  });
                  task.done();
                });
          });

      audit.run();

      async function doTest(should, options) {
        // Test k-rate automation of AudioBufferSourceNode with connected
        // input.
        //
        // A reference source node is created with an automation on the
        // selected AudioParam. For simplicity, we just use a linear ramp from
        // the minValue to the maxValue of the AudioParam.
        //
        // The test node has an input signal connected to the AudioParam.  This
        // input signal is created to match the automation on the reference
        // node.
        //
        // Finally, the output from the two nodes must be identical if k-rate
        // inputs are working correctly.
        //
        // Options parameter is a dictionary with the following required
        // members:
        //   prefix    - prefix to use for the messages.
        //   paramName - Name of the AudioParam to be tested

        let {prefix, paramName, startValue, endValue} = options;

        let context = new OfflineAudioContext({
          numberOfChannels: 2,
          sampleRate: sampleRate,
          length: testDuration * sampleRate
        });

        let merger = new ChannelMergerNode(
            context, {numberOfInputs: context.destination.channelCount});
        merger.connect(context.destination);

        // Linear ramp to use for the buffer sources
        let ramp = createLinearRampBuffer(context, context.length);

        // Create the reference and test nodes.
        let refNode;
        let tstNode;

        const nodeOptions = {buffer: ramp};

        should(
            () => refNode = new AudioBufferSourceNode(context, nodeOptions),
            `${prefix}: refNode = new AudioBufferSourceNode(context, ${
                JSON.stringify(nodeOptions)})`)
            .notThrow();

        should(
            () => tstNode = new AudioBufferSourceNode(context, nodeOptions),
            `${prefix}: tstNode = new AudioBufferSourceNode(context, ${
                JSON.stringify(nodeOptions)})`)
            .notThrow();


        // Automate the AudioParam of the reference node with a linear ramp
        should(
            () => refNode[paramName].setValueAtTime(...startValue),
            `${prefix}: refNode[${paramName}].setValueAtTime(${
                startValue[0]}, ${startValue[1]})`)
            .notThrow();

        should(
            () => refNode[paramName].linearRampToValueAtTime(...endValue),
            `${prefix}: refNode[${paramName}].linearRampToValueAtTime(${
                endValue[0]}, ${endValue[1]})`)
            .notThrow();


        // Create the input node and automate it so that it's output when added
        // to the intrinsic value of the AudioParam we get the same values as
        // the automations on the reference node.

        // Compute the start and end values based on the defaultValue of the
        // param and the desired startValue and endValue.  The input is added to
        // the intrinsic value of the AudioParam, so we need to account for
        // that.

        let mod;
        should(
            () => mod = new ConstantSourceNode(context, {offset: 0}),
            `${prefix}: mod = new ConstantSourceNode(context, {offset: 0})`)
            .notThrow();

        let modStart = startValue[0] - refNode[paramName].defaultValue;
        let modEnd = endValue[0] - refNode[paramName].defaultValue;
        should(
            () => mod.offset.setValueAtTime(modStart, startValue[1]),
            `${prefix}: mod.offset.setValueAtTime(${modStart}, ${
                startValue[1]})`)
            .notThrow();
        should(
            () => mod.offset.linearRampToValueAtTime(modEnd, endValue[1]),
            `${prefix}: mod.offset.linearRampToValueAtTime(${modEnd}, ${
                endValue[1]})`)
            .notThrow();

        // Connect up everything.
        should(
            () => mod.connect(tstNode[paramName]),
            `${prefix}: mod.connect(tstNode[${paramName}])`)
            .notThrow();

        refNode.connect(merger, 0, 0);
        tstNode.connect(merger, 0, 1);

        // Go!
        refNode.start();
        tstNode.start();
        mod.start();

        const buffer = await context.startRendering();
        let expected = buffer.getChannelData(0);
        let actual = buffer.getChannelData(1);

        // Quick sanity check that output isn't zero.  This means we messed up
        // the connections or automations or the buffer source.
        should(expected, `Expected k-rate ${paramName} AudioParam with input`)
            .notBeConstantValueOf(0);
        should(actual, `Actual k-rate ${paramName} AudioParam with input`)
            .notBeConstantValueOf(0);

        // The expected and actual results must be EXACTLY the same.
        should(actual, `k-rate ${paramName} AudioParam with input`)
            .beCloseToArray(expected, {absoluteThreshold: 0});
      }
    </script>
  </body>
</html>