gbemu-part1.js (51364B)
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 20, 20 runGameboy, 21 setupGameboy, 22 tearDownGameboy, 23 null, 24 4)]); 25 26 var decoded_gameboy_rom = null; 27 28 function setupGameboy() { 29 30 // Check if all the types required by the code are supported. 31 // If not, throw exception and quit. 32 if (!(typeof Uint8Array != "undefined" && 33 typeof Int8Array != "undefined" && 34 typeof Float32Array != "undefined" && 35 typeof Int32Array != "undefined") ) { 36 throw "TypedArrayUnsupported"; 37 } 38 decoded_gameboy_rom = base64_decode(gameboy_rom); 39 rom = null; 40 } 41 42 function runGameboy() { 43 start(new GameBoyCanvas(), decoded_gameboy_rom); 44 45 gameboy.instructions = 0; 46 gameboy.totalInstructions = 250000; 47 48 while (gameboy.instructions <= gameboy.totalInstructions) { 49 gameboy.run(); 50 GameBoyAudioNode.run(); 51 } 52 53 resetGlobalVariables(); 54 } 55 56 function tearDownGameboy() { 57 decoded_gameboy_rom = null; 58 expectedGameboyStateStr = null; 59 } 60 61 var expectedGameboyStateStr = 62 '{"registerA":160,"registerB":255,"registerC":255,"registerE":11,' + 63 '"registersHL":51600,"programCounter":24309,"stackPointer":49706,' + 64 '"sumROM":10171578,"sumMemory":3435856,"sumMBCRam":234598,"sumVRam":0}'; 65 66 // Start of browser emulation. 67 68 var GameBoyWindow = { }; 69 70 function GameBoyContext() { 71 this.createBuffer = function() { 72 return new Buffer(); 73 } 74 this.createImageData = function (w, h) { 75 var result = {}; 76 // The following line was updated since Octane 1.0 to avoid OOB access. 77 result.data = new Uint8Array(w * h * 4); 78 return result; 79 } 80 this.putImageData = function (buffer, x, y) { 81 var sum = 0; 82 for (var i = 0; i < buffer.data.length; i++) { 83 sum += i * buffer.data[i]; 84 sum = sum % 1000; 85 } 86 } 87 this.drawImage = function () { } 88 }; 89 90 function GameBoyCanvas() { 91 this.getContext = function() { 92 return new GameBoyContext(); 93 } 94 this.width = 160; 95 this.height = 144; 96 this.style = { visibility: "visibile" }; 97 } 98 99 function cout(message, colorIndex) { 100 } 101 102 function clear_terminal() { 103 } 104 105 var GameBoyAudioNode = { 106 bufferSize : 0, 107 onaudioprocess : null , 108 connect : function () {}, 109 run: function() { 110 var event = {outputBuffer : this.outputBuffer}; 111 this.onaudioprocess(event); 112 } 113 }; 114 115 function GameBoyAudioContext () { 116 this.createBufferSource = function() { 117 return { noteOn : function () {}, connect : function() {}}; 118 } 119 this.sampleRate = 48000; 120 this.destination = {} 121 this.createBuffer = function (channels, len, sampleRate) { 122 return { gain : 1, 123 numberOfChannels : 1, 124 length : 1, 125 duration : 0.000020833333110203966, 126 sampleRate : 48000} 127 } 128 this.createJavaScriptNode = function (bufferSize, inputChannels, outputChannels) { 129 GameBoyAudioNode.bufferSize = bufferSize; 130 GameBoyAudioNode.outputBuffer = { 131 getChannelData : function (i) {return this.channelData[i];}, 132 channelData : [] 133 }; 134 for (var i = 0; i < outputChannels; i++) { 135 GameBoyAudioNode.outputBuffer.channelData[i] = new Float32Array(bufferSize); 136 } 137 return GameBoyAudioNode; 138 } 139 } 140 141 var mock_date_time_counter = 0; 142 143 function new_Date() { 144 return { 145 getTime: function() { 146 mock_date_time_counter += 16; 147 return mock_date_time_counter; 148 } 149 }; 150 } 151 152 // End of browser emulation. 153 154 // Start of helper functions. 155 156 function checkFinalState() { 157 function sum(a) { 158 var result = 0; 159 for (var i = 0; i < a.length; i++) { 160 result += a[i]; 161 } 162 return result; 163 } 164 var state = { 165 registerA: gameboy.registerA, 166 registerB: gameboy.registerB, 167 registerC: gameboy.registerC, 168 registerE: gameboy.registerE, 169 registerF: gameboy.registerF, 170 registersHL: gameboy.registersHL, 171 programCounter: gameboy.programCounter, 172 stackPointer: gameboy.stackPointer, 173 sumROM : sum(gameboy.fromTypedArray(gameboy.ROM)), 174 sumMemory: sum(gameboy.fromTypedArray(gameboy.memory)), 175 sumMBCRam: sum(gameboy.fromTypedArray(gameboy.MBCRam)), 176 sumVRam: sum(gameboy.fromTypedArray(gameboy.VRam)) 177 }; 178 var expectedState = JSON.parse(expectedGameboyStateStr); 179 for (var prop in expectedState) { 180 if (state[prop] !== expectedState[prop]) { 181 var stateStr = JSON.stringify(state); 182 alert("Incorrect final state of processor:\n" + 183 " actual " + stateStr + "\n" + 184 " expected " + expectedGameboyStateStr); 185 } 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.