gain.html (5030B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title> 5 Basic GainNode Functionality 6 </title> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/webaudio/resources/audit-util.js"></script> 10 </head> 11 <body> 12 <script id="layout-test-code"> 13 // Tests that GainNode is properly scaling the gain. We'll render 11 14 // notes, starting at a gain of 1.0, decreasing in gain by 0.1. The 11th 15 // note will be of gain 0.0, so it should be silent (at the end in the 16 // rendered output). 17 // Use a power of two to eliminate any round-off when converting frame to 18 // time. 19 const sampleRate = 32768; 20 // Make sure the buffer duration and spacing are all exact frame lengths 21 // so that the note spacing is also on frame boundaries to eliminate 22 // sub-sample accurate start of a ABSN. 23 const bufferDurationSeconds = Math.floor(0.125 * sampleRate) / sampleRate; 24 const numberOfNotes = 11; 25 // Leave about 20ms of silence, being sure this is an exact frame 26 // duration. 27 const noteSilence = Math.floor(0.020 * sampleRate) / sampleRate; 28 const noteSpacing = bufferDurationSeconds + noteSilence; 29 const lengthInSeconds = numberOfNotes * noteSpacing; 30 31 // Create a stereo AudioBuffer of duration |lengthInSeconds| consisting of 32 // a pure sine wave with the given |frequency|. Both channels contain the 33 // same data. 34 function createSinWaveBuffer(context, lengthInSeconds, frequency) { 35 let audioBuffer = 36 context.createBuffer(2, lengthInSeconds * sampleRate, sampleRate); 37 38 let n = audioBuffer.length; 39 let channelL = audioBuffer.getChannelData(0); 40 let channelR = audioBuffer.getChannelData(1); 41 42 for (let i = 0; i < n; ++i) { 43 channelL[i] = Math.sin(frequency * 2.0 * Math.PI * i / sampleRate); 44 channelR[i] = channelL[i]; 45 } 46 47 return audioBuffer; 48 } 49 50 function playNote(context, time, gain, buffer, merger) { 51 let source = context.createBufferSource(); 52 source.buffer = buffer; 53 54 let gainNode = context.createGain(); 55 gainNode.gain.value = gain; 56 57 let sourceSplitter = context.createChannelSplitter(2); 58 let gainSplitter = context.createChannelSplitter(2); 59 60 // Split the stereo channels from the source output and the gain output 61 // and merge them into the desired channels of the merger. 62 source.connect(gainNode).connect(gainSplitter); 63 source.connect(sourceSplitter); 64 65 gainSplitter.connect(merger, 0, 0); 66 gainSplitter.connect(merger, 1, 1); 67 sourceSplitter.connect(merger, 0, 2); 68 sourceSplitter.connect(merger, 1, 3); 69 70 source.start(time); 71 } 72 73 promise_test(async t => { 74 let context = new OfflineAudioContext( 75 4, sampleRate * lengthInSeconds, sampleRate); 76 77 let merger = new ChannelMergerNode( 78 context, {numberOfInputs: context.destination.channelCount}); 79 merger.connect(context.destination); 80 81 let sinWaveBuffer = createSinWaveBuffer( 82 context, bufferDurationSeconds, 880.0); 83 84 let startTimes = []; 85 let gainValues = []; 86 87 for (let i = 0; i < numberOfNotes; ++i) { 88 let time = i * noteSpacing; 89 let gain = 1.0 - i / (numberOfNotes - 1); 90 startTimes.push(time); 91 gainValues.push(gain); 92 playNote(context, time, gain, sinWaveBuffer, merger); 93 } 94 95 let buffer = await context.startRendering(); 96 97 let actual0 = buffer.getChannelData(0); 98 let actual1 = buffer.getChannelData(1); 99 let reference0 = buffer.getChannelData(2); 100 let reference1 = buffer.getChannelData(3); 101 102 let bufferDurationFrames = 103 Math.ceil(bufferDurationSeconds * context.sampleRate); 104 105 for (let k = 0; k < startTimes.length; ++k) { 106 let startFrame = Math.floor(startTimes[k] * context.sampleRate); 107 let gain = gainValues[k]; 108 for (let n = 0; n < bufferDurationFrames; ++n) { 109 reference0[startFrame + n] *= gain; 110 reference1[startFrame + n] *= gain; 111 } 112 } 113 114 const tolerance = 1.1877e-7; 115 assert_array_approx_equals(actual0, reference0, tolerance, 116 'Left output from gain node should match scaled reference'); 117 assert_array_approx_equals(actual1, reference1, tolerance, 118 'Right output from gain node should match scaled reference'); 119 120 let snr0 = 10 * Math.log10(computeSNR(actual0, reference0)); 121 let snr1 = 10 * Math.log10(computeSNR(actual1, reference1)); 122 123 assert_greater_than_equal( 124 snr0, 148.71, 'Left SNR (in dB) must be ≥ 148.71'); 125 assert_greater_than_equal( 126 snr1, 148.71, 'Right SNR (in dB) must be ≥ 148.71'); 127 }, 'GainNode should scale gains properly across notes'); 128 </script> 129 </body> 130 </html>