sub-sample-buffer-stitching.html (4849B)
1 <!doctype html> 2 <html> 3 <head> 4 <title> 5 Test Sub-Sample Accurate Stitching of ABSNs 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> 13 14 promise_test(async t => { 15 // Sub‑sample stitching with identical context/buffer rates. 16 const sampleRate = 44100; 17 const bufferRate = 44100; 18 const bufferLength = 30; 19 20 // Experimentally determined thresholds. DO NOT relax these values 21 // too far from these values to make the tests pass. 22 const errorThreshold = 9.0957e-5; 23 const snrThreshold = 85.580; 24 25 assert_equals(sampleRate, 44100, 'Test 1: context.sampleRate'); 26 27 const resultBuffer = 28 await testBufferStitching(sampleRate, bufferRate, bufferLength); 29 30 const actual = resultBuffer.getChannelData(0); 31 const expected = resultBuffer.getChannelData(1); 32 33 assert_array_equal_within_eps( 34 actual, 35 expected, 36 errorThreshold, 37 `Stitched sine‑wave buffers at sample rate ${bufferRate}`); 38 39 const SNR = 10 * Math.log10(computeSNR(actual, expected)); 40 41 assert_greater_than_equal( 42 SNR, 43 snrThreshold, 44 `SNR (${SNR.toFixed(3)} dB) should be ≥ ${snrThreshold} dB`); 45 }, 'buffer-stitching-1'); 46 47 promise_test(async t => { 48 // Sub‑sample stitching with differing context/buffer rates. 49 const sampleRate = 44100; 50 const bufferRate = 43800; 51 const bufferLength = 30; 52 53 // Experimentally determined thresholds. DO NOT relax these values 54 // too far from these values to make the tests pass. 55 const errorThreshold = 3.8986e-3; 56 const snrThreshold = 65.737; 57 58 assert_equals(sampleRate, 44100, 'Test 2: context.sampleRate'); 59 60 const resultBuffer = await testBufferStitching( 61 sampleRate, 62 bufferRate, 63 bufferLength); 64 65 const actual = resultBuffer.getChannelData(0); 66 const expected = resultBuffer.getChannelData(1); 67 68 assert_array_equal_within_eps( 69 actual, 70 expected, 71 errorThreshold, 72 `Stitched sine‑wave buffers at sample rate ${bufferRate}`); 73 74 const SNR = 10 * Math.log10(computeSNR(actual, expected)); 75 76 assert_greater_than_equal( 77 SNR, 78 snrThreshold, 79 `SNR (${SNR.toFixed(3)} dB) should be ≥ ${snrThreshold} dB`); 80 }, 'buffer-stitching-2'); 81 82 // Create graph to test stitching of consecutive ABSNs. The context rate 83 // is |sampleRate|, and the buffers have a fixed length of |bufferLength| 84 // and rate of |bufferRate|. The |bufferRate| should not be too different 85 // from |sampleRate| because of interpolation of the buffer to the context 86 // rate. 87 function testBufferStitching(sampleRate, bufferRate, bufferLength) { 88 // The OfflineAudioContext used for rendering. 89 // Channel 0 captures the stitched output. 90 // Channel 1 records the expected reference signal. 91 const context = new OfflineAudioContext({ 92 numberOfChannels: 2, 93 length: sampleRate, 94 sampleRate: sampleRate, 95 }); 96 97 const merger = new ChannelMergerNode( 98 context, {numberOfInputs: context.destination.channelCount}); 99 merger.connect(context.destination); 100 101 // Reference signal (channel 1): pure 440 Hz sine wave 102 const ref = new OscillatorNode(context, {frequency: 440, type: 'sine'}); 103 ref.connect(merger, 0, 1); 104 ref.start(); 105 106 // The test signal is a bunch of short AudioBufferSources containing 107 // bits of a sine wave. 108 const waveSignal = new Float32Array(context.length); 109 const omega = 2 * Math.PI / bufferRate * ref.frequency.value; 110 for (let k = 0; k < context.length; ++k) { 111 waveSignal[k] = Math.sin(omega * k); 112 } 113 114 // Slice the sine wave into many little buffers to be assigned to ABSNs 115 // that are started at the appropriate times to produce a final sine 116 // wave. 117 for (let k = 0; k < context.length; k += bufferLength) { 118 const buffer = 119 new AudioBuffer({length: bufferLength, sampleRate: bufferRate}); 120 121 // Copy the slice into the AudioBuffer’s first (and only) channel. 122 buffer.copyToChannel(waveSignal.slice(k, k + bufferLength), 0); 123 124 const src = new AudioBufferSourceNode(context, {buffer: buffer}); 125 src.connect(merger, 0, 0); 126 src.start(k / bufferRate); 127 } 128 129 return context.startRendering(); 130 } 131 </script> 132 </body> 133 </html>