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