audioparam-close.html (5870B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Test AudioParam events very close in time</title> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/webaudio/resources/audit-util.js"></script> 8 </head> 9 <body> 10 <script> 11 // Largest sample rate that is required to be supported and is a power of 12 // two, to eliminate round-off as much as possible. 13 const sampleRate = 65536; 14 15 // Only need one render quantum for testing. 16 const testFrames = 128; 17 18 // Largest representable single-float number 19 const floatMax = Math.fround(3.4028234663852886e38); 20 21 // epspos is the smallest x such that 1 + x != 1 22 const epspos = 1.1102230246251568e-16; 23 // epsneg is the smallest x such that 1 - x != 1 24 const epsneg = 5.551115123125784e-17; 25 26 promise_test(async t => { 27 const context = new OfflineAudioContext({ 28 numberOfChannels: 1, 29 sampleRate, 30 length: testFrames 31 }); 32 33 const src0 = new ConstantSourceNode(context, {offset: 0}); 34 35 // This should always succeed. We just want to print out a message 36 // that |src0| is a constant source node for the following 37 // processing. 38 assert_equals( 39 src0, src0, 40 'src0 = new ConstantSourceNode(context, {offset: 0}) ' + 41 'should succeed'); 42 43 src0.connect(context.destination); 44 45 // Values for the first event (setValue). |time1| MUST be 0. 46 const time1 = 0; 47 const value1 = 10; 48 49 // Values for the second event (linearRamp). |value2| must be huge, 50 // and |time2| must be small enough that 1/|time2| overflows a 51 // single float. This value is the least positive single float. 52 const time2 = 1.401298464324817e-45; 53 const value2 = floatMax; 54 55 // These should always succeed; the messages are just informational 56 // to show the events that we scheduled. 57 assert_equals( 58 src0.offset.setValueAtTime(value1, time1), 59 src0.offset, 60 `src0.offset.setValueAtTime(${value1}, ${time1}) should ` + 61 `return AudioParam`); 62 assert_equals( 63 src0.offset.linearRampToValueAtTime(value2, time2), 64 src0.offset, 65 `src0.offset.linearRampToValueAtTime(${value2}, ${time2}) should ` + 66 `return AudioParam`); 67 68 src0.start(); 69 70 const renderedBuffer = await context.startRendering(); 71 const output = renderedBuffer.getChannelData(0); 72 73 // Since time1 = 0, the output at frame 0 MUST be value1. 74 assert_equals( 75 output[0], value1, 'Frame 0 should equal initial value set'); 76 77 // Since time2 < 1, output from frame 1 and later must be a 78 // constant. 79 assert_array_constant_value( 80 output.slice(1), 81 value2, 82 'Frames 1+ should equal ramp target value'); 83 }, 'NaN should not occur during extreme linearRampToValueAtTime events'); 84 85 promise_test(async t => { 86 const context = new OfflineAudioContext({ 87 numberOfChannels: 1, 88 sampleRate, 89 length: testFrames 90 }); 91 92 const src1 = new ConstantSourceNode(context, {offset: 0}); 93 94 // This should always succeed. We just want to print out a message 95 // that |src1| is a constant source node for the following 96 // processing. 97 assert_equals( 98 src1, src1, 99 'src1 = new ConstantSourceNode(context, {offset: 0}) ' + 100 'should succeed'); 101 102 src1.connect(context.destination); 103 104 const frameIndex = 1; 105 106 // These time values are arranged so that time1 < frame/sampleRate < 107 // time2. This means we need to interpolate to get a value at given 108 // frame. 109 // 110 // The values are not so important, but |value2| should be huge. 111 const time1 = frameIndex * (1 - epsneg) / context.sampleRate; 112 const value1 = 1e15; 113 114 const time2 = frameIndex * (1 + epspos) / context.sampleRate; 115 const value2 = floatMax; 116 117 assert_equals( 118 src1.offset.setValueAtTime(value1, time1), 119 src1.offset, 120 `src1.offset.setValueAtTime(${value1}, ${time1}) should ` + 121 `return AudioParam`); 122 assert_equals( 123 src1.offset.linearRampToValueAtTime(value2, time2), 124 src1.offset, 125 `src1.offset.linearRampToValueAtTime(${value2}, ${time2}) should ` + 126 `return AudioParam`); 127 128 src1.start(); 129 130 const renderedBuffer = await context.startRendering(); 131 const output = renderedBuffer.getChannelData(0); 132 133 // Sanity check 134 assert_not_equals( 135 time2 - time1, 0, 136 'Sanity check: time1 and time2 should not be equal'); 137 // Because 0 < time1 < 1, output must be 0 at time 0. 138 assert_equals( 139 output[0], 0, 'output[0] should be 0 before any automation'); 140 141 // Because time1 < 1/sampleRate < time2, we need to 142 // interpolate the value between these times to determine the 143 // output at frame 1. 144 const sampleTime = frameIndex / context.sampleRate; 145 const interpolated = 146 value1 + (value2 - value1) * (sampleTime - time1) / (time2 - time1); 147 assert_approx_equals( 148 output[1], 149 interpolated, 0, 150 'Interpolated value at frame 1 should match ramp'); 151 152 // Because 1 < time2 < 2, the output at frame 2 and higher is 153 // constant. 154 assert_array_constant_value( 155 output.slice(2), value2, 'output[2:] should be constant at value2'); 156 }, 'Interpolation of linear ramp between very close time values'); 157 </script> 158 </body> 159 </html>