Add JetStream to PerformanceTests
[WebKit-https.git] / PerformanceTests / JetStream / Octane2 / gbemu-part1.js
1 // Portions copyright 2013 Google, Inc
2
3 // Copyright (C) 2010 - 2012 Grant Galitz
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License version 2 as
6 // published by the Free Software Foundation.
7 // The full license is available at http://www.gnu.org/licenses/gpl.html
8 // This program is distributed in the hope that it will be useful, but
9 // WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 // See the GNU General Public License for more details.
12
13 // The code has been adapted for use as a benchmark by Google.
14
15 var GameboyBenchmark = new BenchmarkSuite('Gameboy', [26288412],
16                                           [new Benchmark('Gameboy',
17                                                          false,
18                                                          false,
19                                                          runGameboy,
20                                                          setupGameboy,
21                                                          tearDownGameboy,
22                                                          null,
23                                                          4)]);
24
25 var decoded_gameboy_rom = null;
26
27 function setupGameboy() {
28
29   // Check if all the types required by the code are supported.
30   // If not, throw exception and quit.
31   if (!(typeof Uint8Array != "undefined" &&
32       typeof Int8Array != "undefined" &&
33       typeof Float32Array != "undefined" &&
34       typeof Int32Array != "undefined") ) {
35     throw "TypedArrayUnsupported";
36   }
37   decoded_gameboy_rom = base64_decode(gameboy_rom);
38   rom = null;
39 }
40
41 function runGameboy() {
42   start(new GameBoyCanvas(), decoded_gameboy_rom);
43
44   gameboy.instructions = 0;
45   gameboy.totalInstructions = 250000;
46
47   while (gameboy.instructions <= gameboy.totalInstructions) {
48     gameboy.run();
49     GameBoyAudioNode.run();
50   }
51
52   resetGlobalVariables();
53 }
54
55 function tearDownGameboy() {
56   decoded_gameboy_rom = null;
57   expectedGameboyStateStr = null;
58 }
59
60 var expectedGameboyStateStr =
61   '{"registerA":160,"registerB":255,"registerC":255,"registerE":11,' +
62   '"registersHL":51600,"programCounter":24309,"stackPointer":49706,' +
63   '"sumROM":10171578,"sumMemory":3435856,"sumMBCRam":234598,"sumVRam":0}';
64
65 // Start of browser emulation.
66
67 var GameBoyWindow = { };
68
69 function GameBoyContext() {
70   this.createBuffer = function() {
71     return new Buffer();
72   }
73   this.createImageData = function (w, h) {
74     var result = {};
75     // The following line was updated since Octane 1.0 to avoid OOB access.
76     result.data = new Uint8Array(w * h * 4);
77     return result;
78   }
79   this.putImageData = function (buffer, x, y) {
80     var sum = 0;
81     for (var i = 0; i < buffer.data.length; i++) {
82       sum += i * buffer.data[i];
83       sum = sum % 1000;
84     }
85   }
86   this.drawImage = function () { }
87 };
88
89 function GameBoyCanvas() {
90   this.getContext = function() {
91     return new GameBoyContext();
92   }
93   this.width = 160;
94   this.height = 144;
95   this.style = { visibility: "visibile" };
96 }
97
98 function cout(message, colorIndex) {
99 }
100
101 function clear_terminal() {
102 }
103
104 var GameBoyAudioNode = {
105   bufferSize : 0,
106   onaudioprocess : null ,
107   connect : function () {},
108   run: function() {
109     var event = {outputBuffer : this.outputBuffer};
110     this.onaudioprocess(event);
111   }
112 };
113
114 function GameBoyAudioContext () {
115   this.createBufferSource = function() {
116     return { noteOn : function () {}, connect : function() {}};
117   }
118   this.sampleRate = 48000;
119   this.destination = {}
120   this.createBuffer = function (channels, len, sampleRate) {
121     return { gain : 1,
122              numberOfChannels : 1,
123              length : 1,
124              duration : 0.000020833333110203966,
125              sampleRate : 48000}
126   }
127   this.createJavaScriptNode = function (bufferSize, inputChannels, outputChannels) {
128     GameBoyAudioNode.bufferSize = bufferSize;
129     GameBoyAudioNode.outputBuffer = {
130         getChannelData : function (i) {return this.channelData[i];},
131         channelData    : []
132     };
133     for (var i = 0; i < outputChannels; i++) {
134       GameBoyAudioNode.outputBuffer.channelData[i] = new Float32Array(bufferSize);
135     }
136     return GameBoyAudioNode;
137   }
138 }
139
140 var mock_date_time_counter = 0;
141
142 function new_Date() {
143   return {
144     getTime: function() {
145       mock_date_time_counter += 16;
146       return mock_date_time_counter;
147     }
148   };
149 }
150
151 // End of browser emulation.
152
153 // Start of helper functions.
154
155 function checkFinalState() {
156   function sum(a) {
157     var result = 0;
158     for (var i = 0; i < a.length; i++) {
159       result += a[i];
160     }
161     return result;
162   }
163   var state = {
164     registerA: gameboy.registerA,
165     registerB: gameboy.registerB,
166     registerC: gameboy.registerC,
167     registerE: gameboy.registerE,
168     registerF: gameboy.registerF,
169     registersHL: gameboy.registersHL,
170     programCounter: gameboy.programCounter,
171     stackPointer: gameboy.stackPointer,
172     sumROM : sum(gameboy.fromTypedArray(gameboy.ROM)),
173     sumMemory: sum(gameboy.fromTypedArray(gameboy.memory)),
174     sumMBCRam: sum(gameboy.fromTypedArray(gameboy.MBCRam)),
175     sumVRam: sum(gameboy.fromTypedArray(gameboy.VRam))
176   }
177   var stateStr = JSON.stringify(state);
178   if (typeof expectedGameboyStateStr != "undefined") {
179     if (stateStr != expectedGameboyStateStr) {
180       alert("Incorrect final state of processor:\n" +
181             " actual   " + stateStr + "\n" +
182             " expected " + expectedGameboyStateStr);
183     }
184   } else {
185     alert(stateStr);
186   }
187 }
188
189
190 function resetGlobalVariables () {
191   //Audio API Event Handler:
192   audioContextHandle = null;
193   audioNode = null;
194   audioSource = null;
195   launchedContext = false;
196   audioContextSampleBuffer = [];
197   resampled = [];
198   webAudioMinBufferSize = 15000;
199   webAudioMaxBufferSize = 25000;
200   webAudioActualSampleRate = 44100;
201   XAudioJSSampleRate = 0;
202   webAudioMono = false;
203   XAudioJSVolume = 1;
204   resampleControl = null;
205   audioBufferSize = 0;
206   resampleBufferStart = 0;
207   resampleBufferEnd = 0;
208   resampleBufferSize = 2;
209
210   gameboy = null;           //GameBoyCore object.
211   gbRunInterval = null;       //GameBoyCore Timer
212 }
213
214
215 // End of helper functions.
216
217 // Original code from Grant Galitz follows.
218 // Modifications by Google are marked in comments.
219
220 // Start of js/other/base64.js file.
221
222 var toBase64 = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
223   "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
224   "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+" , "/", "="];
225 var fromBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
226 function base64(data) {
227   try {
228     // The following line was modified for benchmarking:
229     var base64 = GameBoyWindow.btoa(data);  //Use this native function when it's available, as it's a magnitude faster than the non-native code below.
230   }
231   catch (error) {
232     //Defaulting to non-native base64 encoding...
233     var base64 = "";
234     var dataLength = data.length;
235     if (dataLength > 0) {
236       var bytes = [0, 0, 0];
237       var index = 0;
238       var remainder = dataLength % 3;
239       while (data.length % 3 > 0) {
240         //Make sure we don't do fuzzy math in the next loop...
241         data[data.length] = " ";
242       }
243       while (index < dataLength) {
244         //Keep this loop small for speed.
245         bytes = [data.charCodeAt(index++) & 0xFF, data.charCodeAt(index++) & 0xFF, data.charCodeAt(index++) & 0xFF];
246         base64 += toBase64[bytes[0] >> 2] + toBase64[((bytes[0] & 0x3) << 4) | (bytes[1] >> 4)] + toBase64[((bytes[1] & 0xF) << 2) | (bytes[2] >> 6)] + toBase64[bytes[2] & 0x3F];
247       }
248       if (remainder > 0) {
249         //Fill in the padding and recalulate the trailing six-bit group...
250         base64[base64.length - 1] = "=";
251         if (remainder == 2) {
252           base64[base64.length - 2] = "=";
253           base64[base64.length - 3] = toBase64[(bytes[0] & 0x3) << 4];
254         }
255         else {
256           base64[base64.length - 2] = toBase64[(bytes[1] & 0xF) << 2];
257         }
258       }
259     }
260   }
261   return base64;
262 }
263 function base64_decode(data) {
264   try {
265     // The following line was modified for benchmarking:
266     var decode64 = GameBoyWindow.atob(data);  //Use this native function when it's available, as it's a magnitude faster than the non-native code below.
267   }
268   catch (error) {
269     //Defaulting to non-native base64 decoding...
270     var decode64 = "";
271     var dataLength = data.length;
272     if (dataLength > 3 && dataLength % 4 == 0) {
273       var sixbits = [0, 0, 0, 0];  //Declare this out of the loop, to speed up the ops.
274       var index = 0;
275       while (index < dataLength) {
276         //Keep this loop small for speed.
277         sixbits = [fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++))];
278         decode64 += String.fromCharCode((sixbits[0] << 2) | (sixbits[1] >> 4)) + String.fromCharCode(((sixbits[1] & 0x0F) << 4) | (sixbits[2] >> 2)) + String.fromCharCode(((sixbits[2] & 0x03) << 6) | sixbits[3]);
279       }
280       //Check for the '=' character after the loop, so we don't hose it up.
281       if (sixbits[3] >= 0x40) {
282         decode64.length -= 1;
283         if (sixbits[2] >= 0x40) {
284           decode64.length -= 1;
285         }
286       }
287     }
288   }
289   return decode64;
290 }
291 function to_little_endian_dword(str) {
292   return to_little_endian_word(str) + String.fromCharCode((str >> 16) & 0xFF, (str >> 24) & 0xFF);
293 }
294 function to_little_endian_word(str) {
295   return to_byte(str) + String.fromCharCode((str >> 8) & 0xFF);
296 }
297 function to_byte(str) {
298   return String.fromCharCode(str & 0xFF);
299 }
300 function arrayToBase64(arrayIn) {
301   var binString = "";
302   var length = arrayIn.length;
303   for (var index = 0; index < length; ++index) {
304     if (typeof arrayIn[index] == "number") {
305       binString += String.fromCharCode(arrayIn[index]);
306     }
307   }
308   return base64(binString);
309 }
310 function base64ToArray(b64String) {
311   var binString = base64_decode(b64String);
312   var outArray = [];
313   var length = binString.length;
314   for (var index = 0; index < length;) {
315     outArray.push(binString.charCodeAt(index++) & 0xFF);
316   }
317   return outArray;
318 }
319
320 // End of js/other/base64.js file.
321
322 // Start of js/other/resampler.js file.
323
324 //JavaScript Audio Resampler (c) 2011 - Grant Galitz
325 function Resampler(fromSampleRate, toSampleRate, channels, outputBufferSize, noReturn) {
326   this.fromSampleRate = fromSampleRate;
327   this.toSampleRate = toSampleRate;
328   this.channels = channels | 0;
329   this.outputBufferSize = outputBufferSize;
330   this.noReturn = !!noReturn;
331   this.initialize();
332 }
333 Resampler.prototype.initialize = function () {
334   //Perform some checks:
335   if (this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0) {
336     if (this.fromSampleRate == this.toSampleRate) {
337       //Setup a resampler bypass:
338       this.resampler = this.bypassResampler;    //Resampler just returns what was passed through.
339       this.ratioWeight = 1;
340     }
341     else {
342       //Setup the interpolation resampler:
343       this.compileInterpolationFunction();
344       this.resampler = this.interpolate;      //Resampler is a custom quality interpolation algorithm.
345       this.ratioWeight = this.fromSampleRate / this.toSampleRate;
346       this.tailExists = false;
347       this.lastWeight = 0;
348       this.initializeBuffers();
349     }
350   }
351   else {
352     throw(new Error("Invalid settings specified for the resampler."));
353   }
354 }
355 Resampler.prototype.compileInterpolationFunction = function () {
356   var toCompile = "var bufferLength = Math.min(buffer.length, this.outputBufferSize);\
357   if ((bufferLength % " + this.channels + ") == 0) {\
358     if (bufferLength > 0) {\
359       var ratioWeight = this.ratioWeight;\
360       var weight = 0;";
361   for (var channel = 0; channel < this.channels; ++channel) {
362     toCompile += "var output" + channel + " = 0;"
363   }
364   toCompile += "var actualPosition = 0;\
365       var amountToNext = 0;\
366       var alreadyProcessedTail = !this.tailExists;\
367       this.tailExists = false;\
368       var outputBuffer = this.outputBuffer;\
369       var outputOffset = 0;\
370       var currentPosition = 0;\
371       do {\
372         if (alreadyProcessedTail) {\
373           weight = ratioWeight;";
374   for (channel = 0; channel < this.channels; ++channel) {
375     toCompile += "output" + channel + " = 0;"
376   }
377   toCompile += "}\
378         else {\
379           weight = this.lastWeight;";
380   for (channel = 0; channel < this.channels; ++channel) {
381     toCompile += "output" + channel + " = this.lastOutput[" + channel + "];"
382   }
383   toCompile += "alreadyProcessedTail = true;\
384         }\
385         while (weight > 0 && actualPosition < bufferLength) {\
386           amountToNext = 1 + actualPosition - currentPosition;\
387           if (weight >= amountToNext) {";
388   for (channel = 0; channel < this.channels; ++channel) {
389     toCompile += "output" + channel + " += buffer[actualPosition++] * amountToNext;"
390   }
391   toCompile += "currentPosition = actualPosition;\
392             weight -= amountToNext;\
393           }\
394           else {";
395   for (channel = 0; channel < this.channels; ++channel) {
396     toCompile += "output" + channel + " += buffer[actualPosition" + ((channel > 0) ? (" + " + channel) : "") + "] * weight;"
397   }
398   toCompile += "currentPosition += weight;\
399             weight = 0;\
400             break;\
401           }\
402         }\
403         if (weight == 0) {";
404   for (channel = 0; channel < this.channels; ++channel) {
405     toCompile += "outputBuffer[outputOffset++] = output" + channel + " / ratioWeight;"
406   }
407   toCompile += "}\
408         else {\
409           this.lastWeight = weight;";
410   for (channel = 0; channel < this.channels; ++channel) {
411     toCompile += "this.lastOutput[" + channel + "] = output" + channel + ";"
412   }
413   toCompile += "this.tailExists = true;\
414           break;\
415         }\
416       } while (actualPosition < bufferLength);\
417       return this.bufferSlice(outputOffset);\
418     }\
419     else {\
420       return (this.noReturn) ? 0 : [];\
421     }\
422   }\
423   else {\
424     throw(new Error(\"Buffer was of incorrect sample length.\"));\
425   }";
426   this.interpolate = Function("buffer", toCompile);
427 }
428 Resampler.prototype.bypassResampler = function (buffer) {
429   if (this.noReturn) {
430     //Set the buffer passed as our own, as we don't need to resample it:
431     this.outputBuffer = buffer;
432     return buffer.length;
433   }
434   else {
435     //Just return the buffer passsed:
436     return buffer;
437   }
438 }
439 Resampler.prototype.bufferSlice = function (sliceAmount) {
440   if (this.noReturn) {
441     //If we're going to access the properties directly from this object:
442     return sliceAmount;
443   }
444   else {
445     //Typed array and normal array buffer section referencing:
446     try {
447       return this.outputBuffer.subarray(0, sliceAmount);
448     }
449     catch (error) {
450       try {
451         //Regular array pass:
452         this.outputBuffer.length = sliceAmount;
453         return this.outputBuffer;
454       }
455       catch (error) {
456         //Nightly Firefox 4 used to have the subarray function named as slice:
457         return this.outputBuffer.slice(0, sliceAmount);
458       }
459     }
460   }
461 }
462 Resampler.prototype.initializeBuffers = function () {
463   //Initialize the internal buffer:
464   try {
465     this.outputBuffer = new Float32Array(this.outputBufferSize);
466     this.lastOutput = new Float32Array(this.channels);
467   }
468   catch (error) {
469     this.outputBuffer = [];
470     this.lastOutput = [];
471   }
472 }
473
474 // End of js/other/resampler.js file.
475
476 // Start of js/other/XAudioServer.js file.
477
478 /*Initialize here first:
479   Example:
480     Stereo audio with a sample rate of 70 khz, a minimum buffer of 15000 samples total, a maximum buffer of 25000 samples total and a starting volume level of 1.
481       var parentObj = this;
482       this.audioHandle = new XAudioServer(2, 70000, 15000, 25000, function (sampleCount) {
483         return parentObj.audioUnderRun(sampleCount);
484       }, 1);
485
486   The callback is passed the number of samples requested, while it can return any number of samples it wants back.
487 */
488 function XAudioServer(channels, sampleRate, minBufferSize, maxBufferSize, underRunCallback, volume) {
489   this.audioChannels = (channels == 2) ? 2 : 1;
490   webAudioMono = (this.audioChannels == 1);
491   XAudioJSSampleRate = (sampleRate > 0 && sampleRate <= 0xFFFFFF) ? sampleRate : 44100;
492   webAudioMinBufferSize = (minBufferSize >= (samplesPerCallback << 1) && minBufferSize < maxBufferSize) ? (minBufferSize & ((webAudioMono) ? 0xFFFFFFFF : 0xFFFFFFFE)) : (samplesPerCallback << 1);
493   webAudioMaxBufferSize = (Math.floor(maxBufferSize) > webAudioMinBufferSize + this.audioChannels) ? (maxBufferSize & ((webAudioMono) ? 0xFFFFFFFF : 0xFFFFFFFE)) : (minBufferSize << 1);
494   this.underRunCallback = (typeof underRunCallback == "function") ? underRunCallback : function () {};
495   XAudioJSVolume = (volume >= 0 && volume <= 1) ? volume : 1;
496   this.audioType = -1;
497   this.mozAudioTail = [];
498   this.audioHandleMoz = null;
499   this.audioHandleFlash = null;
500   this.flashInitialized = false;
501   this.mozAudioFound = false;
502   this.initializeAudio();
503 }
504 XAudioServer.prototype.MOZWriteAudio = function (buffer) {
505   //mozAudio:
506   this.MOZWriteAudioNoCallback(buffer);
507   this.MOZExecuteCallback();
508 }
509 XAudioServer.prototype.MOZWriteAudioNoCallback = function (buffer) {
510   //mozAudio:
511   this.writeMozAudio(buffer);
512 }
513 XAudioServer.prototype.callbackBasedWriteAudio = function (buffer) {
514   //Callback-centered audio APIs:
515   this.callbackBasedWriteAudioNoCallback(buffer);
516   this.callbackBasedExecuteCallback();
517 }
518 XAudioServer.prototype.callbackBasedWriteAudioNoCallback = function (buffer) {
519   //Callback-centered audio APIs:
520   var length = buffer.length;
521   for (var bufferCounter = 0; bufferCounter < length && audioBufferSize < webAudioMaxBufferSize;) {
522     audioContextSampleBuffer[audioBufferSize++] = buffer[bufferCounter++];
523   }
524 }
525 /*Pass your samples into here!
526 Pack your samples as a one-dimenional array
527 With the channel samplea packed uniformly.
528 examples:
529     mono - [left, left, left, left]
530     stereo - [left, right, left, right, left, right, left, right]
531 */
532 XAudioServer.prototype.writeAudio = function (buffer) {
533   if (this.audioType == 0) {
534     this.MOZWriteAudio(buffer);
535   }
536   else if (this.audioType == 1) {
537     this.callbackBasedWriteAudio(buffer);
538   }
539   else if (this.audioType == 2) {
540     if (this.checkFlashInit() || launchedContext) {
541       this.callbackBasedWriteAudio(buffer);
542     }
543     else if (this.mozAudioFound) {
544       this.MOZWriteAudio(buffer);
545     }
546   }
547 }
548 /*Pass your samples into here if you don't want automatic callback calling:
549 Pack your samples as a one-dimenional array
550 With the channel samplea packed uniformly.
551 examples:
552     mono - [left, left, left, left]
553     stereo - [left, right, left, right, left, right, left, right]
554 Useful in preventing infinite recursion issues with calling writeAudio inside your callback.
555 */
556 XAudioServer.prototype.writeAudioNoCallback = function (buffer) {
557   if (this.audioType == 0) {
558     this.MOZWriteAudioNoCallback(buffer);
559   }
560   else if (this.audioType == 1) {
561     this.callbackBasedWriteAudioNoCallback(buffer);
562   }
563   else if (this.audioType == 2) {
564     if (this.checkFlashInit() || launchedContext) {
565       this.callbackBasedWriteAudioNoCallback(buffer);
566     }
567     else if (this.mozAudioFound) {
568       this.MOZWriteAudioNoCallback(buffer);
569     }
570   }
571 }
572 //Developer can use this to see how many samples to write (example: minimum buffer allotment minus remaining samples left returned from this function to make sure maximum buffering is done...)
573 //If -1 is returned, then that means metric could not be done.
574 XAudioServer.prototype.remainingBuffer = function () {
575   if (this.audioType == 0) {
576     //mozAudio:
577     return this.samplesAlreadyWritten - this.audioHandleMoz.mozCurrentSampleOffset();
578   }
579   else if (this.audioType == 1) {
580     //WebKit Audio:
581     return (((resampledSamplesLeft() * resampleControl.ratioWeight) >> (this.audioChannels - 1)) << (this.audioChannels - 1)) + audioBufferSize;
582   }
583   else if (this.audioType == 2) {
584     if (this.checkFlashInit() || launchedContext) {
585       //Webkit Audio / Flash Plugin Audio:
586       return (((resampledSamplesLeft() * resampleControl.ratioWeight) >> (this.audioChannels - 1)) << (this.audioChannels - 1)) + audioBufferSize;
587     }
588     else if (this.mozAudioFound) {
589       //mozAudio:
590       return this.samplesAlreadyWritten - this.audioHandleMoz.mozCurrentSampleOffset();
591     }
592   }
593   //Default return:
594   return 0;
595 }
596 XAudioServer.prototype.MOZExecuteCallback = function () {
597   //mozAudio:
598   var samplesRequested = webAudioMinBufferSize - this.remainingBuffer();
599   if (samplesRequested > 0) {
600     this.writeMozAudio(this.underRunCallback(samplesRequested));
601   }
602 }
603 XAudioServer.prototype.callbackBasedExecuteCallback = function () {
604   //WebKit /Flash Audio:
605   var samplesRequested = webAudioMinBufferSize - this.remainingBuffer();
606   if (samplesRequested > 0) {
607     this.callbackBasedWriteAudioNoCallback(this.underRunCallback(samplesRequested));
608   }
609 }
610 //If you just want your callback called for any possible refill (Execution of callback is still conditional):
611 XAudioServer.prototype.executeCallback = function () {
612   if (this.audioType == 0) {
613     this.MOZExecuteCallback();
614   }
615   else if (this.audioType == 1) {
616     this.callbackBasedExecuteCallback();
617   }
618   else if (this.audioType == 2) {
619     if (this.checkFlashInit() || launchedContext) {
620       this.callbackBasedExecuteCallback();
621     }
622     else if (this.mozAudioFound) {
623       this.MOZExecuteCallback();
624     }
625   }
626 }
627 //DO NOT CALL THIS, the lib calls this internally!
628 XAudioServer.prototype.initializeAudio = function () {
629   try {
630     throw (new Error("Select initializeWebAudio case"));  // Line added for benchmarking.
631     this.preInitializeMozAudio();
632     if (navigator.platform == "Linux i686") {
633       //Block out mozaudio usage for Linux Firefox due to moz bugs:
634       throw(new Error(""));
635     }
636     this.initializeMozAudio();
637   }
638   catch (error) {
639     try {
640       this.initializeWebAudio();
641     }
642     catch (error) {
643       try {
644         this.initializeFlashAudio();
645       }
646       catch (error) {
647         throw(new Error("Browser does not support real time audio output."));
648       }
649     }
650   }
651 }
652 XAudioServer.prototype.preInitializeMozAudio = function () {
653   //mozAudio - Synchronous Audio API
654   this.audioHandleMoz = new Audio();
655   this.audioHandleMoz.mozSetup(this.audioChannels, XAudioJSSampleRate);
656   this.samplesAlreadyWritten = 0;
657   var emptySampleFrame = (this.audioChannels == 2) ? [0, 0] : [0];
658   var prebufferAmount = 0;
659   if (navigator.platform != "MacIntel" && navigator.platform != "MacPPC") {  //Mac OS X doesn't experience this moz-bug!
660     while (this.audioHandleMoz.mozCurrentSampleOffset() == 0) {
661       //Mozilla Audio Bugginess Workaround (Firefox freaks out if we don't give it a prebuffer under certain OSes):
662       prebufferAmount += this.audioHandleMoz.mozWriteAudio(emptySampleFrame);
663     }
664     var samplesToDoubleBuffer = prebufferAmount / this.audioChannels;
665     //Double the prebuffering for windows:
666     for (var index = 0; index < samplesToDoubleBuffer; index++) {
667       this.samplesAlreadyWritten += this.audioHandleMoz.mozWriteAudio(emptySampleFrame);
668     }
669   }
670   this.samplesAlreadyWritten += prebufferAmount;
671   webAudioMinBufferSize += this.samplesAlreadyWritten;
672   this.mozAudioFound = true;
673 }
674 XAudioServer.prototype.initializeMozAudio = function () {
675   //Fill in our own buffering up to the minimum specified:
676   this.writeMozAudio(getFloat32(webAudioMinBufferSize));
677   this.audioType = 0;
678 }
679 XAudioServer.prototype.initializeWebAudio = function () {
680   if (launchedContext) {
681     resetCallbackAPIAudioBuffer(webAudioActualSampleRate, samplesPerCallback);
682     this.audioType = 1;
683   }
684   else {
685     throw(new Error(""));
686   }
687 }
688 XAudioServer.prototype.initializeFlashAudio = function () {
689   var existingFlashload = document.getElementById("XAudioJS");
690   if (existingFlashload == null) {
691     var thisObj = this;
692     var mainContainerNode = document.createElement("div");
693     mainContainerNode.setAttribute("style", "position: fixed; bottom: 0px; right: 0px; margin: 0px; padding: 0px; border: none; width: 8px; height: 8px; overflow: hidden; z-index: -1000; ");
694     var containerNode = document.createElement("div");
695     containerNode.setAttribute("style", "position: static; border: none; width: 0px; height: 0px; visibility: hidden; margin: 8px; padding: 0px;");
696     containerNode.setAttribute("id", "XAudioJS");
697     mainContainerNode.appendChild(containerNode);
698     document.getElementsByTagName("body")[0].appendChild(mainContainerNode);
699     swfobject.embedSWF(
700       "XAudioJS.swf",
701       "XAudioJS",
702       "8",
703       "8",
704       "9.0.0",
705       "",
706       {},
707       {"allowscriptaccess":"always"},
708       {"style":"position: static; visibility: hidden; margin: 8px; padding: 0px; border: none"},
709       function (event) {
710         if (event.success) {
711           thisObj.audioHandleFlash = event.ref;
712         }
713         else {
714           thisObj.audioType = 1;
715         }
716       }
717     );
718   }
719   else {
720     this.audioHandleFlash = existingFlashload;
721   }
722   this.audioType = 2;
723 }
724 XAudioServer.prototype.changeVolume = function (newVolume) {
725   if (newVolume >= 0 && newVolume <= 1) {
726     XAudioJSVolume = newVolume;
727     if (this.checkFlashInit()) {
728       this.audioHandleFlash.changeVolume(XAudioJSVolume);
729     }
730     if (this.mozAudioFound) {
731       this.audioHandleMoz.volume = XAudioJSVolume;
732     }
733   }
734 }
735 //Moz Audio Buffer Writing Handler:
736 XAudioServer.prototype.writeMozAudio = function (buffer) {
737   var length = this.mozAudioTail.length;
738   if (length > 0) {
739     var samplesAccepted = this.audioHandleMoz.mozWriteAudio(this.mozAudioTail);
740     this.samplesAlreadyWritten += samplesAccepted;
741     this.mozAudioTail.splice(0, samplesAccepted);
742   }
743   length = Math.min(buffer.length, webAudioMaxBufferSize - this.samplesAlreadyWritten + this.audioHandleMoz.mozCurrentSampleOffset());
744   var samplesAccepted = this.audioHandleMoz.mozWriteAudio(buffer);
745   this.samplesAlreadyWritten += samplesAccepted;
746   for (var index = 0; length > samplesAccepted; --length) {
747     //Moz Audio wants us saving the tail:
748     this.mozAudioTail.push(buffer[index++]);
749   }
750 }
751 //Checks to see if the NPAPI Adobe Flash bridge is ready yet:
752 XAudioServer.prototype.checkFlashInit = function () {
753   if (!this.flashInitialized && this.audioHandleFlash && this.audioHandleFlash.initialize) {
754     this.flashInitialized = true;
755     this.audioHandleFlash.initialize(this.audioChannels, XAudioJSVolume);
756     resetCallbackAPIAudioBuffer(44100, samplesPerCallback);
757   }
758   return this.flashInitialized;
759 }
760 /////////END LIB
761 function getFloat32(size) {
762   try {
763     return new Float32Array(size);
764   }
765   catch (error) {
766     return new Array(size);
767   }
768 }
769 function getFloat32Flat(size) {
770   try {
771     var newBuffer = new Float32Array(size);
772   }
773   catch (error) {
774     var newBuffer = new Array(size);
775     var audioSampleIndice = 0;
776     do {
777       newBuffer[audioSampleIndice] = 0;
778     } while (++audioSampleIndice < size);
779   }
780   return newBuffer;
781 }
782 //Flash NPAPI Event Handler:
783 var samplesPerCallback = 2048;      //Has to be between 2048 and 4096 (If over, then samples are ignored, if under then silence is added).
784 var outputConvert = null;
785 function audioOutputFlashEvent() {    //The callback that flash calls...
786   resampleRefill();
787   return outputConvert();
788 }
789 function generateFlashStereoString() {  //Convert the arrays to one long string for speed.
790   var copyBinaryStringLeft = "";
791   var copyBinaryStringRight = "";
792   for (var index = 0; index < samplesPerCallback && resampleBufferStart != resampleBufferEnd; ++index) {
793     //Sanitize the buffer:
794     copyBinaryStringLeft += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
795     copyBinaryStringRight += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
796     if (resampleBufferStart == resampleBufferSize) {
797       resampleBufferStart = 0;
798     }
799   }
800   return copyBinaryStringLeft + copyBinaryStringRight;
801 }
802 function generateFlashMonoString() {  //Convert the array to one long string for speed.
803   var copyBinaryString = "";
804   for (var index = 0; index < samplesPerCallback && resampleBufferStart != resampleBufferEnd; ++index) {
805     //Sanitize the buffer:
806     copyBinaryString += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
807     if (resampleBufferStart == resampleBufferSize) {
808       resampleBufferStart = 0;
809     }
810   }
811   return copyBinaryString;
812 }
813 //Audio API Event Handler:
814 var audioContextHandle = null;
815 var audioNode = null;
816 var audioSource = null;
817 var launchedContext = false;
818 var audioContextSampleBuffer = [];
819 var resampled = [];
820 var webAudioMinBufferSize = 15000;
821 var webAudioMaxBufferSize = 25000;
822 var webAudioActualSampleRate = 44100;
823 var XAudioJSSampleRate = 0;
824 var webAudioMono = false;
825 var XAudioJSVolume = 1;
826 var resampleControl = null;
827 var audioBufferSize = 0;
828 var resampleBufferStart = 0;
829 var resampleBufferEnd = 0;
830 var resampleBufferSize = 2;
831 function audioOutputEvent(event) {    //Web Audio API callback...
832   var index = 0;
833   var buffer1 = event.outputBuffer.getChannelData(0);
834   var buffer2 = event.outputBuffer.getChannelData(1);
835   resampleRefill();
836   if (!webAudioMono) {
837     //STEREO:
838     while (index < samplesPerCallback && resampleBufferStart != resampleBufferEnd) {
839       buffer1[index] = resampled[resampleBufferStart++] * XAudioJSVolume;
840       buffer2[index++] = resampled[resampleBufferStart++] * XAudioJSVolume;
841       if (resampleBufferStart == resampleBufferSize) {
842         resampleBufferStart = 0;
843       }
844     }
845   }
846   else {
847     //MONO:
848     while (index < samplesPerCallback && resampleBufferStart != resampleBufferEnd) {
849       buffer2[index] = buffer1[index] = resampled[resampleBufferStart++] * XAudioJSVolume;
850       ++index;
851       if (resampleBufferStart == resampleBufferSize) {
852         resampleBufferStart = 0;
853       }
854     }
855   }
856   //Pad with silence if we're underrunning:
857   while (index < samplesPerCallback) {
858     buffer2[index] = buffer1[index] = 0;
859     ++index;
860   }
861 }
862 function resampleRefill() {
863   if (audioBufferSize > 0) {
864     //Resample a chunk of audio:
865     var resampleLength = resampleControl.resampler(getBufferSamples());
866     var resampledResult = resampleControl.outputBuffer;
867     for (var index2 = 0; index2 < resampleLength; ++index2) {
868       resampled[resampleBufferEnd++] = resampledResult[index2];
869       if (resampleBufferEnd == resampleBufferSize) {
870         resampleBufferEnd = 0;
871       }
872       if (resampleBufferStart == resampleBufferEnd) {
873         ++resampleBufferStart;
874         if (resampleBufferStart == resampleBufferSize) {
875           resampleBufferStart = 0;
876         }
877       }
878     }
879     audioBufferSize = 0;
880   }
881 }
882 function resampledSamplesLeft() {
883   return ((resampleBufferStart <= resampleBufferEnd) ? 0 : resampleBufferSize) + resampleBufferEnd - resampleBufferStart;
884 }
885 function getBufferSamples() {
886   //Typed array and normal array buffer section referencing:
887   try {
888     return audioContextSampleBuffer.subarray(0, audioBufferSize);
889   }
890   catch (error) {
891     try {
892       //Regular array pass:
893       audioContextSampleBuffer.length = audioBufferSize;
894       return audioContextSampleBuffer;
895     }
896     catch (error) {
897       //Nightly Firefox 4 used to have the subarray function named as slice:
898       return audioContextSampleBuffer.slice(0, audioBufferSize);
899     }
900   }
901 }
902 //Initialize WebKit Audio /Flash Audio Buffer:
903 function resetCallbackAPIAudioBuffer(APISampleRate, bufferAlloc) {
904   audioContextSampleBuffer = getFloat32(webAudioMaxBufferSize);
905   audioBufferSize = webAudioMaxBufferSize;
906   resampleBufferStart = 0;
907   resampleBufferEnd = 0;
908   resampleBufferSize = Math.max(webAudioMaxBufferSize * Math.ceil(XAudioJSSampleRate / APISampleRate), samplesPerCallback) << 1;
909   if (webAudioMono) {
910     //MONO Handling:
911     resampled = getFloat32Flat(resampleBufferSize);
912     resampleControl = new Resampler(XAudioJSSampleRate, APISampleRate, 1, resampleBufferSize, true);
913     outputConvert = generateFlashMonoString;
914   }
915   else {
916     //STEREO Handling:
917     resampleBufferSize  <<= 1;
918     resampled = getFloat32Flat(resampleBufferSize);
919     resampleControl = new Resampler(XAudioJSSampleRate, APISampleRate, 2, resampleBufferSize, true);
920     outputConvert = generateFlashStereoString;
921   }
922 }
923 //Initialize WebKit Audio:
924 (function () {
925   if (!launchedContext) {
926     try {
927       // The following line was modified for benchmarking:
928       audioContextHandle = new GameBoyAudioContext();              //Create a system audio context.
929     }
930     catch (error) {
931       try {
932         audioContextHandle = new AudioContext();                //Create a system audio context.
933       }
934       catch (error) {
935         return;
936       }
937     }
938     try {
939       audioSource = audioContextHandle.createBufferSource();            //We need to create a false input to get the chain started.
940       audioSource.loop = false;  //Keep this alive forever (Event handler will know when to ouput.)
941       XAudioJSSampleRate = webAudioActualSampleRate = audioContextHandle.sampleRate;
942       audioSource.buffer = audioContextHandle.createBuffer(1, 1, webAudioActualSampleRate);  //Create a zero'd input buffer for the input to be valid.
943       audioNode = audioContextHandle.createJavaScriptNode(samplesPerCallback, 1, 2);      //Create 2 outputs and ignore the input buffer (Just copy buffer 1 over if mono)
944       audioNode.onaudioprocess = audioOutputEvent;                //Connect the audio processing event to a handling function so we can manipulate output
945       audioSource.connect(audioNode);                        //Send and chain the input to the audio manipulation.
946       audioNode.connect(audioContextHandle.destination);              //Send and chain the output of the audio manipulation to the system audio output.
947       audioSource.noteOn(0);                            //Start the loop!
948     }
949     catch (error) {
950       return;
951     }
952     launchedContext = true;
953   }
954 })();
955
956 // End of js/other/XAudioServer.js file.
957
958 // Start of js/other/resize.js file.
959
960 //JavaScript Image Resizer (c) 2012 - Grant Galitz
961 function Resize(widthOriginal, heightOriginal, targetWidth, targetHeight, blendAlpha, interpolationPass) {
962   this.widthOriginal = Math.abs(parseInt(widthOriginal) || 0);
963   this.heightOriginal = Math.abs(parseInt(heightOriginal) || 0);
964   this.targetWidth = Math.abs(parseInt(targetWidth) || 0);
965   this.targetHeight = Math.abs(parseInt(targetHeight) || 0);
966   this.colorChannels = (!!blendAlpha) ? 4 : 3;
967   this.interpolationPass = !!interpolationPass;
968   this.targetWidthMultipliedByChannels = this.targetWidth * this.colorChannels;
969   this.originalWidthMultipliedByChannels = this.widthOriginal * this.colorChannels;
970   this.originalHeightMultipliedByChannels = this.heightOriginal * this.colorChannels;
971   this.widthPassResultSize = this.targetWidthMultipliedByChannels * this.heightOriginal;
972   this.finalResultSize = this.targetWidthMultipliedByChannels * this.targetHeight;
973   this.initialize();
974 }
975 Resize.prototype.initialize = function () {
976   //Perform some checks:
977   if (this.widthOriginal > 0 && this.heightOriginal > 0 && this.targetWidth > 0 && this.targetHeight > 0) {
978     if (this.widthOriginal == this.targetWidth) {
979       //Bypass the width resizer pass:
980       this.resizeWidth = this.bypassResizer;
981     }
982     else {
983       //Setup the width resizer pass:
984       this.ratioWeightWidthPass = this.widthOriginal / this.targetWidth;
985       if (this.ratioWeightWidthPass < 1 && this.interpolationPass) {
986         this.initializeFirstPassBuffers(true);
987         this.resizeWidth = (this.colorChannels == 4) ? this.resizeWidthInterpolatedRGBA : this.resizeWidthInterpolatedRGB;
988       }
989       else {
990         this.initializeFirstPassBuffers(false);
991         this.resizeWidth = (this.colorChannels == 4) ? this.resizeWidthRGBA : this.resizeWidthRGB;
992       }
993     }
994     if (this.heightOriginal == this.targetHeight) {
995       //Bypass the height resizer pass:
996       this.resizeHeight = this.bypassResizer;
997     }
998     else {
999       //Setup the height resizer pass:
1000       this.ratioWeightHeightPass = this.heightOriginal / this.targetHeight;
1001       if (this.ratioWeightHeightPass < 1 && this.interpolationPass) {
1002         this.initializeSecondPassBuffers(true);
1003         this.resizeHeight = this.resizeHeightInterpolated;
1004       }
1005       else {
1006         this.initializeSecondPassBuffers(false);
1007         this.resizeHeight = (this.colorChannels == 4) ? this.resizeHeightRGBA : this.resizeHeightRGB;
1008       }
1009     }
1010   }
1011   else {
1012     throw(new Error("Invalid settings specified for the resizer."));
1013   }
1014 }
1015 Resize.prototype.resizeWidthRGB = function (buffer) {
1016   var ratioWeight = this.ratioWeightWidthPass;
1017   var weight = 0;
1018   var amountToNext = 0;
1019   var actualPosition = 0;
1020   var currentPosition = 0;
1021   var line = 0;
1022   var pixelOffset = 0;
1023   var outputOffset = 0;
1024   var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - 2;
1025   var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - 2;
1026   var output = this.outputWidthWorkBench;
1027   var outputBuffer = this.widthBuffer;
1028   do {
1029     for (line = 0; line < this.originalHeightMultipliedByChannels;) {
1030       output[line++] = 0;
1031       output[line++] = 0;
1032       output[line++] = 0;
1033     }
1034     weight = ratioWeight;
1035     do {
1036       amountToNext = 1 + actualPosition - currentPosition;
1037       if (weight >= amountToNext) {
1038         for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
1039           output[line++] += buffer[pixelOffset++] * amountToNext;
1040           output[line++] += buffer[pixelOffset++] * amountToNext;
1041           output[line++] += buffer[pixelOffset] * amountToNext;
1042         }
1043         currentPosition = actualPosition = actualPosition + 3;
1044         weight -= amountToNext;
1045       }
1046       else {
1047         for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
1048           output[line++] += buffer[pixelOffset++] * weight;
1049           output[line++] += buffer[pixelOffset++] * weight;
1050           output[line++] += buffer[pixelOffset] * weight;
1051         }
1052         currentPosition += weight;
1053         break;
1054       }
1055     } while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
1056     for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
1057       outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1058       outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1059       outputBuffer[pixelOffset] = output[line++] / ratioWeight;
1060     }
1061     outputOffset += 3;
1062   } while (outputOffset < this.targetWidthMultipliedByChannels);
1063   return outputBuffer;
1064 }
1065 Resize.prototype.resizeWidthInterpolatedRGB = function (buffer) {
1066   var ratioWeight = (this.widthOriginal - 1) / this.targetWidth;
1067   var weight = 0;
1068   var finalOffset = 0;
1069   var pixelOffset = 0;
1070   var outputBuffer = this.widthBuffer;
1071   for (var targetPosition = 0; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += 3, weight += ratioWeight) {
1072     //Calculate weightings:
1073     secondWeight = weight % 1;
1074     firstWeight = 1 - secondWeight;
1075     //Interpolate:
1076     for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * 3; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
1077       outputBuffer[finalOffset] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 3] * secondWeight);
1078       outputBuffer[finalOffset + 1] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
1079       outputBuffer[finalOffset + 2] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
1080     }
1081   }
1082   return outputBuffer;
1083 }
1084 Resize.prototype.resizeWidthRGBA = function (buffer) {
1085   var ratioWeight = this.ratioWeightWidthPass;
1086   var weight = 0;
1087   var amountToNext = 0;
1088   var actualPosition = 0;
1089   var currentPosition = 0;
1090   var line = 0;
1091   var pixelOffset = 0;
1092   var outputOffset = 0;
1093   var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - 3;
1094   var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - 3;
1095   var output = this.outputWidthWorkBench;
1096   var outputBuffer = this.widthBuffer;
1097   do {
1098     for (line = 0; line < this.originalHeightMultipliedByChannels;) {
1099       output[line++] = 0;
1100       output[line++] = 0;
1101       output[line++] = 0;
1102       output[line++] = 0;
1103     }
1104     weight = ratioWeight;
1105     do {
1106       amountToNext = 1 + actualPosition - currentPosition;
1107       if (weight >= amountToNext) {
1108         for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
1109           output[line++] += buffer[pixelOffset++] * amountToNext;
1110           output[line++] += buffer[pixelOffset++] * amountToNext;
1111           output[line++] += buffer[pixelOffset++] * amountToNext;
1112           output[line++] += buffer[pixelOffset] * amountToNext;
1113         }
1114         currentPosition = actualPosition = actualPosition + 4;
1115         weight -= amountToNext;
1116       }
1117       else {
1118         for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
1119           output[line++] += buffer[pixelOffset++] * weight;
1120           output[line++] += buffer[pixelOffset++] * weight;
1121           output[line++] += buffer[pixelOffset++] * weight;
1122           output[line++] += buffer[pixelOffset] * weight;
1123         }
1124         currentPosition += weight;
1125         break;
1126       }
1127     } while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
1128     for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
1129       outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1130       outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1131       outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1132       outputBuffer[pixelOffset] = output[line++] / ratioWeight;
1133     }
1134     outputOffset += 4;
1135   } while (outputOffset < this.targetWidthMultipliedByChannels);
1136   return outputBuffer;
1137 }
1138 Resize.prototype.resizeWidthInterpolatedRGBA = function (buffer) {
1139   var ratioWeight = (this.widthOriginal - 1) / this.targetWidth;
1140   var weight = 0;
1141   var finalOffset = 0;
1142   var pixelOffset = 0;
1143   var outputBuffer = this.widthBuffer;
1144   for (var targetPosition = 0; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += 4, weight += ratioWeight) {
1145     //Calculate weightings:
1146     secondWeight = weight % 1;
1147     firstWeight = 1 - secondWeight;
1148     //Interpolate:
1149     for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * 4; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
1150       outputBuffer[finalOffset] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
1151       outputBuffer[finalOffset + 1] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
1152       outputBuffer[finalOffset + 2] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 6] * secondWeight);
1153       outputBuffer[finalOffset + 3] = (buffer[pixelOffset + 3] * firstWeight) + (buffer[pixelOffset + 7] * secondWeight);
1154     }
1155   }
1156   return outputBuffer;
1157 }
1158 Resize.prototype.resizeHeightRGB = function (buffer) {
1159   var ratioWeight = this.ratioWeightHeightPass;
1160   var weight = 0;
1161   var amountToNext = 0;
1162   var actualPosition = 0;
1163   var currentPosition = 0;
1164   var pixelOffset = 0;
1165   var outputOffset = 0;
1166   var output = this.outputHeightWorkBench;
1167   var outputBuffer = this.heightBuffer;
1168   do {
1169     for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1170       output[pixelOffset++] = 0;
1171       output[pixelOffset++] = 0;
1172       output[pixelOffset++] = 0;
1173     }
1174     weight = ratioWeight;
1175     do {
1176       amountToNext = 1 + actualPosition - currentPosition;
1177       if (weight >= amountToNext) {
1178         for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1179           output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1180           output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1181           output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1182         }
1183         currentPosition = actualPosition;
1184         weight -= amountToNext;
1185       }
1186       else {
1187         for (pixelOffset = 0, amountToNext = actualPosition; pixelOffset < this.targetWidthMultipliedByChannels;) {
1188           output[pixelOffset++] += buffer[amountToNext++] * weight;
1189           output[pixelOffset++] += buffer[amountToNext++] * weight;
1190           output[pixelOffset++] += buffer[amountToNext++] * weight;
1191         }
1192         currentPosition += weight;
1193         break;
1194       }
1195     } while (weight > 0 && actualPosition < this.widthPassResultSize);
1196     for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1197       outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1198       outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1199       outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1200     }
1201   } while (outputOffset < this.finalResultSize);
1202   return outputBuffer;
1203 }
1204 Resize.prototype.resizeHeightInterpolated = function (buffer) {
1205   var ratioWeight = (this.heightOriginal - 1) / this.targetHeight;
1206   var weight = 0;
1207   var finalOffset = 0;
1208   var pixelOffset = 0;
1209   var pixelOffsetAccumulated = 0;
1210   var pixelOffsetAccumulated2 = 0;
1211   var outputBuffer = this.heightBuffer;
1212   do {
1213     //Calculate weightings:
1214     secondWeight = weight % 1;
1215     firstWeight = 1 - secondWeight;
1216     //Interpolate:
1217     pixelOffsetAccumulated = Math.floor(weight) * this.targetWidthMultipliedByChannels;
1218     pixelOffsetAccumulated2 = pixelOffsetAccumulated + this.targetWidthMultipliedByChannels;
1219     for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
1220       outputBuffer[finalOffset++] = (buffer[pixelOffsetAccumulated + pixelOffset] * firstWeight) + (buffer[pixelOffsetAccumulated2 + pixelOffset] * secondWeight);
1221     }
1222     weight += ratioWeight;
1223   } while (finalOffset < this.finalResultSize);
1224   return outputBuffer;
1225 }
1226 Resize.prototype.resizeHeightRGBA = function (buffer) {
1227   var ratioWeight = this.ratioWeightHeightPass;
1228   var weight = 0;
1229   var amountToNext = 0;
1230   var actualPosition = 0;
1231   var currentPosition = 0;
1232   var pixelOffset = 0;
1233   var outputOffset = 0;
1234   var output = this.outputHeightWorkBench;
1235   var outputBuffer = this.heightBuffer;
1236   do {
1237     for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1238       output[pixelOffset++] = 0;
1239       output[pixelOffset++] = 0;
1240       output[pixelOffset++] = 0;
1241       output[pixelOffset++] = 0;
1242     }
1243     weight = ratioWeight;
1244     do {
1245       amountToNext = 1 + actualPosition - currentPosition;
1246       if (weight >= amountToNext) {
1247         for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1248           output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1249           output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1250           output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1251           output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1252         }
1253         currentPosition = actualPosition;
1254         weight -= amountToNext;
1255       }
1256       else {
1257         for (pixelOffset = 0, amountToNext = actualPosition; pixelOffset < this.targetWidthMultipliedByChannels;) {
1258           output[pixelOffset++] += buffer[amountToNext++] * weight;
1259           output[pixelOffset++] += buffer[amountToNext++] * weight;
1260           output[pixelOffset++] += buffer[amountToNext++] * weight;
1261           output[pixelOffset++] += buffer[amountToNext++] * weight;
1262         }
1263         currentPosition += weight;
1264         break;
1265       }
1266     } while (weight > 0 && actualPosition < this.widthPassResultSize);
1267     for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1268       outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1269       outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1270       outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1271       outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1272     }
1273   } while (outputOffset < this.finalResultSize);
1274   return outputBuffer;
1275 }
1276 Resize.prototype.resizeHeightInterpolatedRGBA = function (buffer) {
1277   var ratioWeight = (this.heightOriginal - 1) / this.targetHeight;
1278   var weight = 0;
1279   var finalOffset = 0;
1280   var pixelOffset = 0;
1281   var outputBuffer = this.heightBuffer;
1282   while (pixelOffset < this.finalResultSize) {
1283     //Calculate weightings:
1284     secondWeight = weight % 1;
1285     firstWeight = 1 - secondWeight;
1286     //Interpolate:
1287     for (pixelOffset = Math.floor(weight) * 4; pixelOffset < this.targetWidthMultipliedByChannels; pixelOffset += 4) {
1288       outputBuffer[finalOffset++] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
1289       outputBuffer[finalOffset++] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
1290       outputBuffer[finalOffset++] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 6] * secondWeight);
1291       outputBuffer[finalOffset++] = (buffer[pixelOffset + 3] * firstWeight) + (buffer[pixelOffset + 7] * secondWeight);
1292     }
1293     weight += ratioWeight;
1294   }
1295   return outputBuffer;
1296 }
1297 Resize.prototype.resize = function (buffer) {
1298   return this.resizeHeight(this.resizeWidth(buffer));
1299 }
1300 Resize.prototype.bypassResizer = function (buffer) {
1301   //Just return the buffer passsed:
1302   return buffer;
1303 }
1304 Resize.prototype.initializeFirstPassBuffers = function (BILINEARAlgo) {
1305   //Initialize the internal width pass buffers:
1306   this.widthBuffer = this.generateFloatBuffer(this.widthPassResultSize);
1307   if (!BILINEARAlgo) {
1308     this.outputWidthWorkBench = this.generateFloatBuffer(this.originalHeightMultipliedByChannels);
1309   }
1310 }
1311 Resize.prototype.initializeSecondPassBuffers = function (BILINEARAlgo) {
1312   //Initialize the internal height pass buffers:
1313   this.heightBuffer = this.generateUint8Buffer(this.finalResultSize);
1314   if (!BILINEARAlgo) {
1315     this.outputHeightWorkBench = this.generateFloatBuffer(this.targetWidthMultipliedByChannels);
1316   }
1317 }
1318 Resize.prototype.generateFloatBuffer = function (bufferLength) {
1319   //Generate a float32 typed array buffer:
1320   try {
1321     return new Float32Array(bufferLength);
1322   }
1323   catch (error) {
1324     return [];
1325   }
1326 }
1327 Resize.prototype.generateUint8Buffer = function (bufferLength) {
1328   //Generate a uint8 typed array buffer:
1329   try {
1330     return this.checkForOperaMathBug(new Uint8Array(bufferLength));
1331   }
1332   catch (error) {
1333     return [];
1334   }
1335 }
1336 Resize.prototype.checkForOperaMathBug = function (typedArray) {
1337   typedArray[0] = -1;
1338   typedArray[0] >>= 0;
1339   if (typedArray[0] != 0xFF) {
1340     return [];
1341   }
1342   else {
1343     return typedArray;
1344   }
1345 }
1346
1347 // End of js/other/resize.js file.
1348
1349 // Remaining files are in gbemu-part2.js, since they run in strict mode.
1350