1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/test/webaudio.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,239 @@ 1.4 +// Helpers for Web Audio tests 1.5 + 1.6 +function expectException(func, exceptionCode) { 1.7 + var threw = false; 1.8 + try { 1.9 + func(); 1.10 + } catch (ex) { 1.11 + threw = true; 1.12 + ok(ex instanceof DOMException, "Expect a DOM exception"); 1.13 + is(ex.code, exceptionCode, "Expect the correct exception code"); 1.14 + } 1.15 + ok(threw, "The exception was thrown"); 1.16 +} 1.17 + 1.18 +function expectNoException(func) { 1.19 + var threw = false; 1.20 + try { 1.21 + func(); 1.22 + } catch (ex) { 1.23 + threw = true; 1.24 + } 1.25 + ok(!threw, "An exception was not thrown"); 1.26 +} 1.27 + 1.28 +function expectTypeError(func) { 1.29 + var threw = false; 1.30 + try { 1.31 + func(); 1.32 + } catch (ex) { 1.33 + threw = true; 1.34 + ok(ex instanceof TypeError, "Expect a TypeError"); 1.35 + } 1.36 + ok(threw, "The exception was thrown"); 1.37 +} 1.38 + 1.39 +function fuzzyCompare(a, b) { 1.40 + return Math.abs(a - b) < 9e-3; 1.41 +} 1.42 + 1.43 +function compareChannels(buf1, buf2, 1.44 + /*optional*/ length, 1.45 + /*optional*/ sourceOffset, 1.46 + /*optional*/ destOffset, 1.47 + /*optional*/ skipLengthCheck) { 1.48 + if (!skipLengthCheck) { 1.49 + is(buf1.length, buf2.length, "Channels must have the same length"); 1.50 + } 1.51 + sourceOffset = sourceOffset || 0; 1.52 + destOffset = destOffset || 0; 1.53 + if (length == undefined) { 1.54 + length = buf1.length - sourceOffset; 1.55 + } 1.56 + var difference = 0; 1.57 + var maxDifference = 0; 1.58 + var firstBadIndex = -1; 1.59 + for (var i = 0; i < length; ++i) { 1.60 + if (!fuzzyCompare(buf1[i + sourceOffset], buf2[i + destOffset])) { 1.61 + difference++; 1.62 + maxDifference = Math.max(maxDifference, Math.abs(buf1[i + sourceOffset] - buf2[i + destOffset])); 1.63 + if (firstBadIndex == -1) { 1.64 + firstBadIndex = i; 1.65 + } 1.66 + } 1.67 + }; 1.68 + 1.69 + is(difference, 0, "maxDifference: " + maxDifference + 1.70 + ", first bad index: " + firstBadIndex + 1.71 + " with test-data offset " + sourceOffset + " and expected-data offset " + 1.72 + destOffset + "; corresponding values " + buf1[firstBadIndex + sourceOffset] + 1.73 + " and " + buf2[firstBadIndex + destOffset] + " --- differences"); 1.74 +} 1.75 + 1.76 +function compareBuffers(got, expected) { 1.77 + if (got.numberOfChannels != expected.numberOfChannels) { 1.78 + is(got.numberOfChannels, expected.numberOfChannels, 1.79 + "Correct number of buffer channels"); 1.80 + return; 1.81 + } 1.82 + if (got.length != expected.length) { 1.83 + is(got.length, expected.length, 1.84 + "Correct buffer length"); 1.85 + return; 1.86 + } 1.87 + if (got.sampleRate != expected.sampleRate) { 1.88 + is(got.sampleRate, expected.sampleRate, 1.89 + "Correct sample rate"); 1.90 + return; 1.91 + } 1.92 + 1.93 + for (var i = 0; i < got.numberOfChannels; ++i) { 1.94 + compareChannels(got.getChannelData(i), expected.getChannelData(i), 1.95 + got.length, 0, 0, true); 1.96 + } 1.97 +} 1.98 + 1.99 +function getEmptyBuffer(context, length) { 1.100 + return context.createBuffer(gTest.numberOfChannels, length, context.sampleRate); 1.101 +} 1.102 + 1.103 +/** 1.104 + * This function assumes that the test file defines a single gTest variable with 1.105 + * the following properties and methods: 1.106 + * 1.107 + * + numberOfChannels: optional property which specifies the number of channels 1.108 + * in the output. The default value is 2. 1.109 + * + createGraph: mandatory method which takes a context object and does 1.110 + * everything needed in order to set up the Web Audio graph. 1.111 + * This function returns the node to be inspected. 1.112 + * + createGraphAsync: async version of createGraph. This function takes 1.113 + * a callback which should be called with an argument 1.114 + * set to the node to be inspected when the callee is 1.115 + * ready to proceed with the test. Either this function 1.116 + * or createGraph must be provided. 1.117 + * + createExpectedBuffers: optional method which takes a context object and 1.118 + * returns either one expected buffer or an array of 1.119 + * them, designating what is expected to be observed 1.120 + * in the output. If omitted, the output is expected 1.121 + * to be silence. All buffers must have the same 1.122 + * length, which must be a bufferSize supported by 1.123 + * ScriptProcessorNode. This function is guaranteed 1.124 + * to be called before createGraph. 1.125 + * + length: property equal to the total number of frames which we are waiting 1.126 + * to see in the output, mandatory if createExpectedBuffers is not 1.127 + * provided, in which case it must be a bufferSize supported by 1.128 + * ScriptProcessorNode (256, 512, 1024, 2048, 4096, 8192, or 16384). 1.129 + * If createExpectedBuffers is provided then this must be equal to 1.130 + * the number of expected buffers * the expected buffer length. 1.131 + * 1.132 + * + skipOfflineContextTests: optional. when true, skips running tests on an offline 1.133 + * context by circumventing testOnOfflineContext. 1.134 + */ 1.135 +function runTest() 1.136 +{ 1.137 + function done() { 1.138 + SimpleTest.finish(); 1.139 + } 1.140 + 1.141 + SimpleTest.waitForExplicitFinish(); 1.142 + function runTestFunction () { 1.143 + if (!gTest.numberOfChannels) { 1.144 + gTest.numberOfChannels = 2; // default 1.145 + } 1.146 + 1.147 + var testLength; 1.148 + 1.149 + function runTestOnContext(context, callback, testOutput) { 1.150 + if (!gTest.createExpectedBuffers) { 1.151 + // Assume that the output is silence 1.152 + var expectedBuffers = getEmptyBuffer(context, gTest.length); 1.153 + } else { 1.154 + var expectedBuffers = gTest.createExpectedBuffers(context); 1.155 + } 1.156 + if (!(expectedBuffers instanceof Array)) { 1.157 + expectedBuffers = [expectedBuffers]; 1.158 + } 1.159 + var expectedFrames = 0; 1.160 + for (var i = 0; i < expectedBuffers.length; ++i) { 1.161 + is(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels, 1.162 + "Correct number of channels for expected buffer " + i); 1.163 + expectedFrames += expectedBuffers[i].length; 1.164 + } 1.165 + if (gTest.length && gTest.createExpectedBuffers) { 1.166 + is(expectedFrames, gTest.length, "Correct number of expected frames"); 1.167 + } 1.168 + 1.169 + if (gTest.createGraphAsync) { 1.170 + gTest.createGraphAsync(context, function(nodeToInspect) { 1.171 + testOutput(nodeToInspect, expectedBuffers, callback); 1.172 + }); 1.173 + } else { 1.174 + testOutput(gTest.createGraph(context), expectedBuffers, callback); 1.175 + } 1.176 + } 1.177 + 1.178 + function testOnNormalContext(callback) { 1.179 + function testOutput(nodeToInspect, expectedBuffers, callback) { 1.180 + testLength = 0; 1.181 + var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels); 1.182 + nodeToInspect.connect(sp); 1.183 + sp.connect(context.destination); 1.184 + sp.onaudioprocess = function(e) { 1.185 + var expectedBuffer = expectedBuffers.shift(); 1.186 + testLength += expectedBuffer.length; 1.187 + compareBuffers(e.inputBuffer, expectedBuffer); 1.188 + if (expectedBuffers.length == 0) { 1.189 + sp.onaudioprocess = null; 1.190 + callback(); 1.191 + } 1.192 + }; 1.193 + } 1.194 + var context = new AudioContext(); 1.195 + runTestOnContext(context, callback, testOutput); 1.196 + } 1.197 + 1.198 + function testOnOfflineContext(callback, sampleRate) { 1.199 + function testOutput(nodeToInspect, expectedBuffers, callback) { 1.200 + nodeToInspect.connect(context.destination); 1.201 + context.oncomplete = function(e) { 1.202 + var samplesSeen = 0; 1.203 + while (expectedBuffers.length) { 1.204 + var expectedBuffer = expectedBuffers.shift(); 1.205 + is(e.renderedBuffer.numberOfChannels, expectedBuffer.numberOfChannels, 1.206 + "Correct number of input buffer channels"); 1.207 + for (var i = 0; i < e.renderedBuffer.numberOfChannels; ++i) { 1.208 + compareChannels(e.renderedBuffer.getChannelData(i), 1.209 + expectedBuffer.getChannelData(i), 1.210 + expectedBuffer.length, 1.211 + samplesSeen, 1.212 + undefined, 1.213 + true); 1.214 + } 1.215 + samplesSeen += expectedBuffer.length; 1.216 + } 1.217 + callback(); 1.218 + }; 1.219 + context.startRendering(); 1.220 + } 1.221 + 1.222 + var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate); 1.223 + runTestOnContext(context, callback, testOutput); 1.224 + } 1.225 + 1.226 + testOnNormalContext(function() { 1.227 + if (!gTest.skipOfflineContextTests) { 1.228 + testOnOfflineContext(function() { 1.229 + testOnOfflineContext(done, 44100); 1.230 + }, 48000); 1.231 + } else { 1.232 + done(); 1.233 + } 1.234 + }); 1.235 + }; 1.236 + 1.237 + if (document.readyState !== 'complete') { 1.238 + addLoadEvent(runTestFunction); 1.239 + } else { 1.240 + runTestFunction(); 1.241 + } 1.242 +}