summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/adding-events.html
blob: ab527b6695cef3f17fa2e41e6d2dc6fa1b36c32a (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
<!doctype html>
<html>
  <head>
    <title>Adding Events</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="/webaudio/resources/audio-param.js"></script>
  </head>

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

      // Arbitrary power of two to eliminate round-off in computing time from
      // frame.
      const sampleRate = 8192;

      audit.define(
          {
            label: 'linearRamp',
            description: 'Insert linearRamp after running for some time'
          },
          (task, should) => {
            testInsertion(should, {
              method: 'linearRampToValueAtTime',
              prefix: 'linearRamp'
            }).then(() => task.done());
          });

      audit.define(
          {
            label: 'expoRamp',
            description: 'Insert expoRamp after running for some time'
          },
          (task, should) => {
            testInsertion(should, {
              method: 'exponentialRampToValueAtTime',
              prefix: 'expoRamp'
            }).then(() => task.done());
          });

      // Test insertion of an event in the middle of rendering.
      //
      // options dictionary:
      //   method - automation method to test
      //   prefix - string to use for prefixing messages
      function testInsertion(should, options) {
        let {method, prefix} = options;

        // Channel 0 is the output for the test, and channel 1 is the
        // reference output.
        let context = new OfflineAudioContext(
            {numberOfChannels: 2, length: sampleRate, sampleRate: sampleRate});
        let merger = new ChannelMergerNode(
            context, {numberOfChannels: context.destination.channelCount});

        merger.connect(context.destination);

        // Initial value and final values of the source node
        let initialValue = 1;
        let finalValue = 2;

        // Set up the node for the automations under test
        let src = new ConstantSourceNode(context, {offset: initialValue});
        src.connect(merger, 0, 0);

        // Set initial event to occur at this time.  Keep it in the first
        // render quantum.
        const initialEventTime = 64 / context.sampleRate;
        should(
            () => src.offset.setValueAtTime(initialValue, initialEventTime),
            `${prefix}: setValueAtTime(${initialValue}, ${initialEventTime})`)
            .notThrow();

        // Let time pass and then add a new event with time in the future.
        let insertAtFrame = 512;
        let insertTime = insertAtFrame / context.sampleRate;
        let automationEndFrame = 1024 + 64;
        let automationEndTime = automationEndFrame / context.sampleRate;
        context.suspend(insertTime)
            .then(() => {
              should(
                  () => src.offset[method](finalValue, automationEndTime),
                  `${prefix}: At time ${insertTime} scheduling ${method}(${
                      finalValue}, ${automationEndTime})`)
                  .notThrow();
            })
            .then(() => context.resume());

        // Set up graph for the reference result.  Automate the source with
        // the events scheduled from the beginning.  Let the gain node
        // simulate the insertion of the event above.  This is done by
        // setting the gain to 1 at the insertion time.
        let srcRef = new ConstantSourceNode(context, {offset: 1});
        let g = new GainNode(context, {gain: 0});
        srcRef.connect(g).connect(merger, 0, 1);
        srcRef.offset.setValueAtTime(initialValue, initialEventTime);
        srcRef.offset[method](finalValue, automationEndTime);

        // Allow everything through after |insertFrame| frames.
        g.gain.setValueAtTime(1, insertTime);

        // Go!
        src.start();
        srcRef.start();

        return context.startRendering().then(audioBuffer => {
          let actual = audioBuffer.getChannelData(0);
          let expected = audioBuffer.getChannelData(1);

          // Verify that the output is 1 until we reach
          // insertAtFrame. Ignore the expected data because that always
          // produces 1.
          should(
              actual.slice(0, insertAtFrame),
              `${prefix}: output[0:${insertAtFrame - 1}]`)
              .beConstantValueOf(initialValue);

          // Verify ramp is correct by comparing it to the expected
          // data.
          should(
              actual.slice(
                  insertAtFrame, automationEndFrame - insertAtFrame + 1),
              `${prefix}: output[${insertAtFrame}:${
                  automationEndFrame - insertAtFrame}]`)
              .beCloseToArray(
                  expected.slice(
                      insertAtFrame, automationEndFrame - insertAtFrame + 1),
                  {absoluteThreshold: 0, numberOfArrayElements: 0});

          // Verify final output has the expected value
          should(
              actual.slice(automationEndFrame),
              `${prefix}: output[${automationEndFrame}:]`)
              .beConstantValueOf(finalValue);
        })
      }

      audit.run();
    </script>
  </body>
</html>