| <!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> |