// Portions copyright 2013 Google, Inc
// Copyright (C) 2010 - 2012 Grant Galitz
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
// The full license is available at http://www.gnu.org/licenses/gpl.html
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
// The code has been adapted for use as a benchmark by Google.
var GameboyBenchmark =
new BenchmarkSuite(
'Gameboy' , [
26288412 ],
[
new Benchmark(
'Gameboy' ,
false ,
false ,
20 ,
runGameboy,
setupGameboy,
tearDownGameboy,
null ,
4 )]);
var decoded_gameboy_rom =
null ;
function setupGameboy() {
// Check if all the types required by the code are supported.
// If not, throw exception and quit.
if (!(
typeof Uint8Array !=
"undefined" &&
typeof Int8Array !=
"undefined" &&
typeof Float32Array !=
"undefined" &&
typeof Int32Array !=
"undefined" ) ) {
throw "TypedArrayUnsupported" ;
}
decoded_gameboy_rom = base64_decode(gameboy_rom);
rom =
null ;
}
function runGameboy() {
start(
new GameBoyCanvas(), decoded_gameboy_rom);
gameboy.instructions =
0 ;
gameboy.totalInstructions =
250000 ;
while (gameboy.instructions <= gameboy.totalInstructions) {
gameboy.run();
GameBoyAudioNode.run();
}
resetGlobalVariables();
}
function tearDownGameboy() {
decoded_gameboy_rom =
null ;
expectedGameboyStateStr =
null ;
}
var expectedGameboyStateStr =
'{"registerA":160,"registerB":255,"registerC":255,"registerE":11,' +
'"registersHL":51600,"programCounter":24309,"stackPointer":49706,' +
'"sumROM":10171578,"sumMemory":3435856,"sumMBCRam":234598,"sumVRam":0}' ;
// Start of browser emulation.
var GameBoyWindow = { };
function GameBoyContext() {
this .createBuffer =
function () {
return new Buffer();
}
this .createImageData =
function (w, h) {
var result = {};
// The following line was updated since Octane 1.0 to avoid OOB access.
result.data =
new Uint8Array(w * h *
4 );
return result;
}
this .putImageData =
function (buffer, x, y) {
var sum =
0 ;
for (
var i =
0 ; i < buffer.data.length; i++) {
sum += i * buffer.data[i];
sum = sum %
1000 ;
}
}
this .drawImage =
function () { }
};
function GameBoyCanvas() {
this .getContext =
function () {
return new GameBoyContext();
}
this .width =
160 ;
this .height =
144 ;
this .style = { visibility:
"visibile" };
}
function cout(message, colorIndex) {
}
function clear_terminal() {
}
var GameBoyAudioNode = {
bufferSize :
0 ,
onaudioprocess :
null ,
connect :
function () {},
run:
function () {
var event = {outputBuffer :
this .outputBuffer};
this .onaudioprocess(event);
}
};
function GameBoyAudioContext () {
this .createBufferSource =
function () {
return { noteOn :
function () {}, connect :
function () {}};
}
this .sampleRate =
48000 ;
this .destination = {}
this .createBuffer =
function (channels, len, sampleRate) {
return { gain :
1 ,
numberOfChannels :
1 ,
length :
1 ,
duration :
0 .
000020833333110203966 ,
sampleRate :
48000 }
}
this .createJavaScriptNode =
function (bufferSize, inputChannels, outputChannels) {
GameBoyAudioNode.bufferSize = bufferSize;
GameBoyAudioNode.outputBuffer = {
getChannelData :
function (i) {
return this .channelData[i];},
channelData : []
};
for (
var i =
0 ; i < outputChannels; i++) {
GameBoyAudioNode.outputBuffer.channelData[i] =
new Float32Array(bufferSize);
}
return GameBoyAudioNode;
}
}
var mock_date_time_counter =
0 ;
function new_Date() {
return {
getTime:
function () {
mock_date_time_counter +=
16 ;
return mock_date_time_counter;
}
};
}
// End of browser emulation.
// Start of helper functions.
function checkFinalState() {
function sum(a) {
var result =
0 ;
for (
var i =
0 ; i < a.length; i++) {
result += a[i];
}
return result;
}
var state = {
registerA: gameboy.registerA,
registerB: gameboy.registerB,
registerC: gameboy.registerC,
registerE: gameboy.registerE,
registerF: gameboy.registerF,
registersHL: gameboy.registersHL,
programCounter: gameboy.programCounter,
stackPointer: gameboy.stackPointer,
sumROM : sum(gameboy.fromTypedArray(gameboy.ROM)),
sumMemory: sum(gameboy.fromTypedArray(gameboy.memory)),
sumMBCRam: sum(gameboy.fromTypedArray(gameboy.MBCRam)),
sumVRam: sum(gameboy.fromTypedArray(gameboy.VRam))
};
var expectedState = JSON.parse(expectedGameboyStateStr);
for (
var prop in expectedState) {
if (state[prop] !== expectedState[prop]) {
var stateStr = JSON.stringify(state);
alert(
"Incorrect final state of processor:\n" +
" actual " + stateStr +
"\n" +
" expected " + expectedGameboyStateStr);
}
}
}
function resetGlobalVariables () {
//Audio API Event Handler:
audioContextHandle =
null ;
audioNode =
null ;
audioSource =
null ;
launchedContext =
false ;
audioContextSampleBuffer = [];
resampled = [];
webAudioMinBufferSize =
15000 ;
webAudioMaxBufferSize =
25000 ;
webAudioActualSampleRate =
44100 ;
XAudioJSSampleRate =
0 ;
webAudioMono =
false ;
XAudioJSVolume =
1 ;
resampleControl =
null ;
audioBufferSize =
0 ;
resampleBufferStart =
0 ;
resampleBufferEnd =
0 ;
resampleBufferSize =
2 ;
gameboy =
null ;
//GameBoyCore object.
gbRunInterval =
null ;
//GameBoyCore Timer
}
// End of helper functions.
// Original code from Grant Galitz follows.
// Modifications by Google are marked in comments.
// Start of js/other/base64.js file.
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" ,
"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" ,
"0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "+" , "/" , "=" ];
var fromBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ;
function base64(data) {
try {
// The following line was modified for benchmarking:
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.
}
catch (error) {
//Defaulting to non-native base64 encoding...
var base64 = "" ;
var dataLength = data.length;
if (dataLength > 0 ) {
var bytes = [0 , 0 , 0 ];
var index = 0 ;
var remainder = dataLength % 3 ;
while (data.length % 3 > 0 ) {
//Make sure we don't do fuzzy math in the next loop...
data[data.length] = " " ;
}
while (index < dataLength) {
//Keep this loop small for speed.
bytes = [data.charCodeAt(index++) & 0 xFF, data.charCodeAt(index++) & 0 xFF, data.charCodeAt(index++) & 0 xFF];
base64 += toBase64[bytes[0 ] >> 2 ] + toBase64[((bytes[0 ] & 0 x3) << 4 ) | (bytes[1 ] >> 4 )] + toBase64[((bytes[1 ] & 0 xF) << 2 ) | (bytes[2 ] >> 6 )] + toBase64[bytes[2 ] & 0 x3F];
}
if (remainder > 0 ) {
//Fill in the padding and recalulate the trailing six-bit group...
base64[base64.length - 1 ] = "=" ;
if (remainder == 2 ) {
base64[base64.length - 2 ] = "=" ;
base64[base64.length - 3 ] = toBase64[(bytes[0 ] & 0 x3) << 4 ];
}
else {
base64[base64.length - 2 ] = toBase64[(bytes[1 ] & 0 xF) << 2 ];
}
}
}
}
return base64;
}
function base64_decode(data) {
try {
// The following line was modified for benchmarking:
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.
}
catch (error) {
//Defaulting to non-native base64 decoding...
var decode64 = "" ;
var dataLength = data.length;
if (dataLength > 3 && dataLength % 4 == 0 ) {
var sixbits = [0 , 0 , 0 , 0 ]; //Declare this out of the loop, to speed up the ops.
var index = 0 ;
while (index < dataLength) {
//Keep this loop small for speed.
sixbits = [fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++))];
decode64 += String.fromCharCode((sixbits[0 ] << 2 ) | (sixbits[1 ] >> 4 )) + String.fromCharCode(((sixbits[1 ] & 0 x0F) << 4 ) | (sixbits[2 ] >> 2 )) + String.fromCharCode(((sixbits[2 ] & 0 x03) << 6 ) | sixbits[3 ]);
}
//Check for the '=' character after the loop, so we don't hose it up.
if (sixbits[3 ] >= 0 x40) {
decode64.length -= 1 ;
if (sixbits[2 ] >= 0 x40) {
decode64.length -= 1 ;
}
}
}
}
return decode64;
}
function to_little_endian_dword(str) {
return to_little_endian_word(str) + String.fromCharCode((str >> 16 ) & 0 xFF, (str >> 24 ) & 0 xFF);
}
function to_little_endian_word(str) {
return to_byte(str) + String.fromCharCode((str >> 8 ) & 0 xFF);
}
function to_byte(str) {
return String.fromCharCode(str & 0 xFF);
}
function arrayToBase64(arrayIn) {
var binString = "" ;
var length = arrayIn.length;
for (var index = 0 ; index < length; ++index) {
if (typeof arrayIn[index] == "number" ) {
binString += String.fromCharCode(arrayIn[index]);
}
}
return base64(binString);
}
function base64ToArray(b64String) {
var binString = base64_decode(b64String);
var outArray = [];
var length = binString.length;
for (var index = 0 ; index < length;) {
outArray.push(binString.charCodeAt(index++) & 0 xFF);
}
return outArray;
}
// End of js/other/base64.js file.
// Start of js/other/resampler.js file.
//JavaScript Audio Resampler (c) 2011 - Grant Galitz
function Resampler(fromSampleRate, toSampleRate, channels, outputBufferSize, noReturn) {
this .fromSampleRate = fromSampleRate;
this .toSampleRate = toSampleRate;
this .channels = channels | 0 ;
this .outputBufferSize = outputBufferSize;
this .noReturn = !!noReturn;
this .initialize();
}
Resampler.prototype.initialize = function () {
//Perform some checks:
if (this .fromSampleRate > 0 && this .toSampleRate > 0 && this .channels > 0 ) {
if (this .fromSampleRate == this .toSampleRate) {
//Setup a resampler bypass:
this .resampler = this .bypassResampler; //Resampler just returns what was passed through.
this .ratioWeight = 1 ;
}
else {
//Setup the interpolation resampler:
this .compileInterpolationFunction();
this .resampler = this .interpolate; //Resampler is a custom quality interpolation algorithm.
this .ratioWeight = this .fromSampleRate / this .toSampleRate;
this .tailExists = false ;
this .lastWeight = 0 ;
this .initializeBuffers();
}
}
else {
throw (new Error("Invalid settings specified for the resampler." ));
}
}
Resampler.prototype.compileInterpolationFunction = function () {
var toCompile = "var bufferLength = Math.min(buffer.length, this.outputBufferSize);\
if ((bufferLength % " + this.channels + " ) == 0 ) {\
if (bufferLength > 0 ) {\
var ratioWeight = this .ratioWeight;\
var weight = 0 ;";
for (var channel = 0 ; channel < this .channels; ++channel) {
toCompile += "var output" + channel + " = 0;"
}
toCompile += "var actualPosition = 0;\
var amountToNext = 0 ;\
var alreadyProcessedTail = !this .tailExists;\
this .tailExists = false ;\
var outputBuffer = this .outputBuffer;\
var outputOffset = 0 ;\
var currentPosition = 0 ;\
do {\
if (alreadyProcessedTail) {\
weight = ratioWeight;";
for (channel = 0 ; channel < this .channels; ++channel) {
toCompile += "output" + channel + " = 0;"
}
toCompile += "}\
else {\
weight = this .lastWeight;";
for (channel = 0 ; channel < this .channels; ++channel) {
toCompile += "output" + channel + " = this.lastOutput[" + channel + "];"
}
toCompile += "alreadyProcessedTail = true;\
}\
while (weight > 0 && actualPosition < bufferLength) {\
amountToNext = 1 + actualPosition - currentPosition;\
if (weight >= amountToNext) {";
for (channel = 0 ; channel < this .channels; ++channel) {
toCompile += "output" + channel + " += buffer[actualPosition++] * amountToNext;"
}
toCompile += "currentPosition = actualPosition;\
weight -= amountToNext;\
}\
else {";
for (channel = 0 ; channel < this .channels; ++channel) {
toCompile += "output" + channel + " += buffer[actualPosition" + ((channel > 0 ) ? (" + " + channel) : "" ) + "] * weight;"
}
toCompile += "currentPosition += weight;\
weight = 0 ;\
break ;\
}\
}\
if (weight == 0 ) {";
for (channel = 0 ; channel < this .channels; ++channel) {
toCompile += "outputBuffer[outputOffset++] = output" + channel + " / ratioWeight;"
}
toCompile += "}\
else {\
this .lastWeight = weight;";
for (channel = 0 ; channel < this .channels; ++channel) {
toCompile += "this.lastOutput[" + channel + "] = output" + channel + ";"
}
toCompile += "this.tailExists = true;\
break ;\
}\
} while (actualPosition < bufferLength);\
return this .bufferSlice(outputOffset);\
}\
else {\
return (this .noReturn) ? 0 : [];\
}\
}\
else {\
throw (new Error(\"Buffer was of incorrect sample length.\" ));\
}";
this .interpolate = Function ("buffer" , toCompile);
}
Resampler.prototype.bypassResampler = function (buffer) {
if (this .noReturn) {
//Set the buffer passed as our own, as we don't need to resample it:
this .outputBuffer = buffer;
return buffer.length;
}
else {
//Just return the buffer passsed:
return buffer;
}
}
Resampler.prototype.bufferSlice = function (sliceAmount) {
if (this .noReturn) {
//If we're going to access the properties directly from this object:
return sliceAmount;
}
else {
//Typed array and normal array buffer section referencing:
try {
return this .outputBuffer.subarray(0 , sliceAmount);
}
catch (error) {
try {
//Regular array pass:
this .outputBuffer.length = sliceAmount;
return this .outputBuffer;
}
catch (error) {
//Nightly Firefox 4 used to have the subarray function named as slice:
return this .outputBuffer.slice(0 , sliceAmount);
}
}
}
}
Resampler.prototype.initializeBuffers = function () {
//Initialize the internal buffer:
try {
this .outputBuffer = new Float32Array(this .outputBufferSize);
this .lastOutput = new Float32Array(this .channels);
}
catch (error) {
this .outputBuffer = [];
this .lastOutput = [];
}
}
// End of js/other/resampler.js file.
// Start of js/other/XAudioServer.js file.
/*Initialize here first:
Example :
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 .
var parentObj = this ;
this . audioHandle = new XAudioServer ( 2 , 70000 , 15000 , 25000 , function ( sampleCount ) {
return parentObj . audioUnderRun ( sampleCount ) ;
} , 1 ) ;
The callback is passed the number of samples requested , while it can return any number of samples it wants back .
*/
function XAudioServer(channels, sampleRate, minBufferSize, maxBufferSize, underRunCallback, volume) {
this .audioChannels = (channels == 2 ) ? 2 : 1 ;
webAudioMono = (this .audioChannels == 1 );
XAudioJSSampleRate = (sampleRate > 0 && sampleRate <= 0 xFFFFFF) ? sampleRate : 44100 ;
webAudioMinBufferSize = (minBufferSize >= (samplesPerCallback << 1 ) && minBufferSize < maxBufferSize) ? (minBufferSize & ((webAudioMono) ? 0 xFFFFFFFF : 0 xFFFFFFFE)) : (samplesPerCallback << 1 );
webAudioMaxBufferSize = (Math.floor(maxBufferSize) > webAudioMinBufferSize + this .audioChannels) ? (maxBufferSize & ((webAudioMono) ? 0 xFFFFFFFF : 0 xFFFFFFFE)) : (minBufferSize << 1 );
this .underRunCallback = (typeof underRunCallback == "function" ) ? underRunCallback : function () {};
XAudioJSVolume = (volume >= 0 && volume <= 1 ) ? volume : 1 ;
this .audioType = -1 ;
this .mozAudioTail = [];
this .audioHandleMoz = null ;
this .audioHandleFlash = null ;
this .flashInitialized = false ;
this .mozAudioFound = false ;
this .initializeAudio();
}
XAudioServer.prototype.MOZWriteAudio = function (buffer) {
//mozAudio:
this .MOZWriteAudioNoCallback(buffer);
this .MOZExecuteCallback();
}
XAudioServer.prototype.MOZWriteAudioNoCallback = function (buffer) {
//mozAudio:
this .writeMozAudio(buffer);
}
XAudioServer.prototype.callbackBasedWriteAudio = function (buffer) {
//Callback-centered audio APIs:
this .callbackBasedWriteAudioNoCallback(buffer);
this .callbackBasedExecuteCallback();
}
XAudioServer.prototype.callbackBasedWriteAudioNoCallback = function (buffer) {
//Callback-centered audio APIs:
var length = buffer.length;
for (var bufferCounter = 0 ; bufferCounter < length && audioBufferSize < webAudioMaxBufferSize;) {
audioContextSampleBuffer[audioBufferSize++] = buffer[bufferCounter++];
}
}
/*Pass your samples into here!
Pack your samples as a one - dimenional array
With the channel samplea packed uniformly .
examples :
mono - [ left , left , left , left ]
stereo - [ left , right , left , right , left , right , left , right ]
*/
XAudioServer.prototype.writeAudio = function (buffer) {
if (this .audioType == 0 ) {
this .MOZWriteAudio(buffer);
}
else if (this .audioType == 1 ) {
this .callbackBasedWriteAudio(buffer);
}
else if (this .audioType == 2 ) {
if (this .checkFlashInit() || launchedContext) {
this .callbackBasedWriteAudio(buffer);
}
else if (this .mozAudioFound) {
this .MOZWriteAudio(buffer);
}
}
}
/*Pass your samples into here if you don't want automatic callback calling:
Pack your samples as a one - dimenional array
With the channel samplea packed uniformly .
examples :
mono - [ left , left , left , left ]
stereo - [ left , right , left , right , left , right , left , right ]
Useful in preventing infinite recursion issues with calling writeAudio inside your callback .
*/
XAudioServer.prototype.writeAudioNoCallback = function (buffer) {
if (this .audioType == 0 ) {
this .MOZWriteAudioNoCallback(buffer);
}
else if (this .audioType == 1 ) {
this .callbackBasedWriteAudioNoCallback(buffer);
}
else if (this .audioType == 2 ) {
if (this .checkFlashInit() || launchedContext) {
this .callbackBasedWriteAudioNoCallback(buffer);
}
else if (this .mozAudioFound) {
this .MOZWriteAudioNoCallback(buffer);
}
}
}
//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...)
//If -1 is returned, then that means metric could not be done.
XAudioServer.prototype.remainingBuffer = function () {
if (this .audioType == 0 ) {
//mozAudio:
return this .samplesAlreadyWritten - this .audioHandleMoz.mozCurrentSampleOffset();
}
else if (this .audioType == 1 ) {
//WebKit Audio:
return (((resampledSamplesLeft() * resampleControl.ratioWeight) >> (this .audioChannels - 1 )) << (this .audioChannels - 1 )) + audioBufferSize;
}
else if (this .audioType == 2 ) {
if (this .checkFlashInit() || launchedContext) {
//Webkit Audio / Flash Plugin Audio:
return (((resampledSamplesLeft() * resampleControl.ratioWeight) >> (this .audioChannels - 1 )) << (this .audioChannels - 1 )) + audioBufferSize;
}
else if (this .mozAudioFound) {
//mozAudio:
return this .samplesAlreadyWritten - this .audioHandleMoz.mozCurrentSampleOffset();
}
}
//Default return:
return 0 ;
}
XAudioServer.prototype.MOZExecuteCallback = function () {
//mozAudio:
var samplesRequested = webAudioMinBufferSize - this .remainingBuffer();
if (samplesRequested > 0 ) {
this .writeMozAudio(this .underRunCallback(samplesRequested));
}
}
XAudioServer.prototype.callbackBasedExecuteCallback = function () {
//WebKit /Flash Audio:
var samplesRequested = webAudioMinBufferSize - this .remainingBuffer();
if (samplesRequested > 0 ) {
this .callbackBasedWriteAudioNoCallback(this .underRunCallback(samplesRequested));
}
}
//If you just want your callback called for any possible refill (Execution of callback is still conditional):
XAudioServer.prototype.executeCallback = function () {
if (this .audioType == 0 ) {
this .MOZExecuteCallback();
}
else if (this .audioType == 1 ) {
this .callbackBasedExecuteCallback();
}
else if (this .audioType == 2 ) {
if (this .checkFlashInit() || launchedContext) {
this .callbackBasedExecuteCallback();
}
else if (this .mozAudioFound) {
this .MOZExecuteCallback();
}
}
}
//DO NOT CALL THIS, the lib calls this internally!
XAudioServer.prototype.initializeAudio = function () {
try {
throw (new Error("Select initializeWebAudio case" )); // Line added for benchmarking.
this .preInitializeMozAudio();
if (navigator.platform == "Linux i686" ) {
//Block out mozaudio usage for Linux Firefox due to moz bugs:
throw (new Error("" ));
}
this .initializeMozAudio();
}
catch (error) {
try {
this .initializeWebAudio();
}
catch (error) {
try {
this .initializeFlashAudio();
}
catch (error) {
throw (new Error("Browser does not support real time audio output." ));
}
}
}
}
XAudioServer.prototype.preInitializeMozAudio = function () {
//mozAudio - Synchronous Audio API
this .audioHandleMoz = new Audio();
this .audioHandleMoz.mozSetup(this .audioChannels, XAudioJSSampleRate);
this .samplesAlreadyWritten = 0 ;
var emptySampleFrame = (this .audioChannels == 2 ) ? [0 , 0 ] : [0 ];
var prebufferAmount = 0 ;
if (navigator.platform != "MacIntel" && navigator.platform != "MacPPC" ) { //Mac OS X doesn't experience this moz-bug!
while (this .audioHandleMoz.mozCurrentSampleOffset() == 0 ) {
//Mozilla Audio Bugginess Workaround (Firefox freaks out if we don't give it a prebuffer under certain OSes):
prebufferAmount += this .audioHandleMoz.mozWriteAudio(emptySampleFrame);
}
var samplesToDoubleBuffer = prebufferAmount / this .audioChannels;
//Double the prebuffering for windows:
for (var index = 0 ; index < samplesToDoubleBuffer; index++) {
this .samplesAlreadyWritten += this .audioHandleMoz.mozWriteAudio(emptySampleFrame);
}
}
this .samplesAlreadyWritten += prebufferAmount;
webAudioMinBufferSize += this .samplesAlreadyWritten;
this .mozAudioFound = true ;
}
XAudioServer.prototype.initializeMozAudio = function () {
//Fill in our own buffering up to the minimum specified:
this .writeMozAudio(getFloat32(webAudioMinBufferSize));
this .audioType = 0 ;
}
XAudioServer.prototype.initializeWebAudio = function () {
if (launchedContext) {
resetCallbackAPIAudioBuffer(webAudioActualSampleRate, samplesPerCallback);
this .audioType = 1 ;
}
else {
throw (new Error("" ));
}
}
XAudioServer.prototype.initializeFlashAudio = function () {
var existingFlashload = document.getElementById("XAudioJS" );
if (existingFlashload == null ) {
var thisObj = this ;
var mainContainerNode = document.createElement("div" );
mainContainerNode.setAttribute("style" , "position: fixed; bottom: 0px; right: 0px; margin: 0px; padding: 0px; border: none; width: 8px; height: 8px; overflow: hidden; z-index: -1000; " );
var containerNode = document.createElement("div" );
containerNode.setAttribute("style" , "position: static; border: none; width: 0px; height: 0px; visibility: hidden; margin: 8px; padding: 0px;" );
containerNode.setAttribute("id" , "XAudioJS" );
mainContainerNode.appendChild(containerNode);
document.getElementsByTagName("body" )[0 ].appendChild(mainContainerNode);
swfobject.embedSWF(
"XAudioJS.swf" ,
"XAudioJS" ,
"8" ,
"8" ,
"9.0.0" ,
"" ,
{},
{"allowscriptaccess" :"always" },
{"style" :"position: static; visibility: hidden; margin: 8px; padding: 0px; border: none" },
function (event) {
if (event.success) {
thisObj.audioHandleFlash = event.ref;
}
else {
thisObj.audioType = 1 ;
}
}
);
}
else {
this .audioHandleFlash = existingFlashload;
}
this .audioType = 2 ;
}
XAudioServer.prototype.changeVolume = function (newVolume) {
if (newVolume >= 0 && newVolume <= 1 ) {
XAudioJSVolume = newVolume;
if (this .checkFlashInit()) {
this .audioHandleFlash.changeVolume(XAudioJSVolume);
}
if (this .mozAudioFound) {
this .audioHandleMoz.volume = XAudioJSVolume;
}
}
}
//Moz Audio Buffer Writing Handler:
XAudioServer.prototype.writeMozAudio = function (buffer) {
var length = this .mozAudioTail.length;
if (length > 0 ) {
var samplesAccepted = this .audioHandleMoz.mozWriteAudio(this .mozAudioTail);
this .samplesAlreadyWritten += samplesAccepted;
this .mozAudioTail.splice(0 , samplesAccepted);
}
length = Math.min(buffer.length, webAudioMaxBufferSize - this .samplesAlreadyWritten + this .audioHandleMoz.mozCurrentSampleOffset());
var samplesAccepted = this .audioHandleMoz.mozWriteAudio(buffer);
this .samplesAlreadyWritten += samplesAccepted;
for (var index = 0 ; length > samplesAccepted; --length) {
//Moz Audio wants us saving the tail:
this .mozAudioTail.push(buffer[index++]);
}
}
//Checks to see if the NPAPI Adobe Flash bridge is ready yet:
XAudioServer.prototype.checkFlashInit = function () {
if (!this .flashInitialized && this .audioHandleFlash && this .audioHandleFlash.initialize) {
this .flashInitialized = true ;
this .audioHandleFlash.initialize(this .audioChannels, XAudioJSVolume);
resetCallbackAPIAudioBuffer(44100 , samplesPerCallback);
}
return this .flashInitialized;
}
/////////END LIB
function getFloat32(size) {
try {
return new Float32Array(size);
}
catch (error) {
return new Array(size);
}
}
function getFloat32Flat(size) {
try {
var newBuffer = new Float32Array(size);
}
catch (error) {
var newBuffer = new Array(size);
var audioSampleIndice = 0 ;
do {
newBuffer[audioSampleIndice] = 0 ;
} while (++audioSampleIndice < size);
}
return newBuffer;
}
//Flash NPAPI Event Handler:
var samplesPerCallback = 2048 ; //Has to be between 2048 and 4096 (If over, then samples are ignored, if under then silence is added).
var outputConvert = null ;
function audioOutputFlashEvent() { //The callback that flash calls...
resampleRefill();
return outputConvert();
}
function generateFlashStereoString() { //Convert the arrays to one long string for speed.
var copyBinaryStringLeft = "" ;
var copyBinaryStringRight = "" ;
for (var index = 0 ; index < samplesPerCallback && resampleBufferStart != resampleBufferEnd; ++index) {
//Sanitize the buffer:
copyBinaryStringLeft += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1 , 0 ), 2 ) * 0 x3FFF) | 0 ) + 0 x3000);
copyBinaryStringRight += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1 , 0 ), 2 ) * 0 x3FFF) | 0 ) + 0 x3000);
if (resampleBufferStart == resampleBufferSize) {
resampleBufferStart = 0 ;
}
}
return copyBinaryStringLeft + copyBinaryStringRight;
}
function generateFlashMonoString() { //Convert the array to one long string for speed.
var copyBinaryString = "" ;
for (var index = 0 ; index < samplesPerCallback && resampleBufferStart != resampleBufferEnd; ++index) {
//Sanitize the buffer:
copyBinaryString += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1 , 0 ), 2 ) * 0 x3FFF) | 0 ) + 0 x3000);
if (resampleBufferStart == resampleBufferSize) {
resampleBufferStart = 0 ;
}
}
return copyBinaryString;
}
//Audio API Event Handler:
var audioContextHandle = null ;
var audioNode = null ;
var audioSource = null ;
var launchedContext = false ;
var audioContextSampleBuffer = [];
var resampled = [];
var webAudioMinBufferSize = 15000 ;
var webAudioMaxBufferSize = 25000 ;
var webAudioActualSampleRate = 44100 ;
var XAudioJSSampleRate = 0 ;
var webAudioMono = false ;
var XAudioJSVolume = 1 ;
var resampleControl = null ;
var audioBufferSize = 0 ;
var resampleBufferStart = 0 ;
var resampleBufferEnd = 0 ;
var resampleBufferSize = 2 ;
function audioOutputEvent(event) { //Web Audio API callback...
var index = 0 ;
var buffer1 = event.outputBuffer.getChannelData(0 );
var buffer2 = event.outputBuffer.getChannelData(1 );
resampleRefill();
if (!webAudioMono) {
//STEREO:
while (index < samplesPerCallback && resampleBufferStart != resampleBufferEnd) {
buffer1[index] = resampled[resampleBufferStart++] * XAudioJSVolume;
buffer2[index++] = resampled[resampleBufferStart++] * XAudioJSVolume;
if (resampleBufferStart == resampleBufferSize) {
resampleBufferStart = 0 ;
}
}
}
else {
//MONO:
while (index < samplesPerCallback && resampleBufferStart != resampleBufferEnd) {
buffer2[index] = buffer1[index] = resampled[resampleBufferStart++] * XAudioJSVolume;
++index;
if (resampleBufferStart == resampleBufferSize) {
resampleBufferStart = 0 ;
}
}
}
//Pad with silence if we're underrunning:
while (index < samplesPerCallback) {
buffer2[index] = buffer1[index] = 0 ;
++index;
}
}
function resampleRefill() {
if (audioBufferSize > 0 ) {
//Resample a chunk of audio:
var resampleLength = resampleControl.resampler(getBufferSamples());
var resampledResult = resampleControl.outputBuffer;
for (var index2 = 0 ; index2 < resampleLength; ++index2) {
resampled[resampleBufferEnd++] = resampledResult[index2];
if (resampleBufferEnd == resampleBufferSize) {
resampleBufferEnd = 0 ;
}
if (resampleBufferStart == resampleBufferEnd) {
++resampleBufferStart;
if (resampleBufferStart == resampleBufferSize) {
resampleBufferStart = 0 ;
}
}
}
audioBufferSize = 0 ;
}
}
function resampledSamplesLeft() {
return ((resampleBufferStart <= resampleBufferEnd) ? 0 : resampleBufferSize) + resampleBufferEnd - resampleBufferStart;
}
function getBufferSamples() {
//Typed array and normal array buffer section referencing:
try {
return audioContextSampleBuffer.subarray(0 , audioBufferSize);
}
catch (error) {
try {
//Regular array pass:
audioContextSampleBuffer.length = audioBufferSize;
return audioContextSampleBuffer;
}
catch (error) {
//Nightly Firefox 4 used to have the subarray function named as slice:
return audioContextSampleBuffer.slice(0 , audioBufferSize);
}
}
}
//Initialize WebKit Audio /Flash Audio Buffer:
function resetCallbackAPIAudioBuffer(APISampleRate, bufferAlloc) {
audioContextSampleBuffer = getFloat32(webAudioMaxBufferSize);
audioBufferSize = webAudioMaxBufferSize;
resampleBufferStart = 0 ;
resampleBufferEnd = 0 ;
resampleBufferSize = Math.max(webAudioMaxBufferSize * Math.ceil(XAudioJSSampleRate / APISampleRate), samplesPerCallback) << 1 ;
if (webAudioMono) {
//MONO Handling:
resampled = getFloat32Flat(resampleBufferSize);
resampleControl = new Resampler(XAudioJSSampleRate, APISampleRate, 1 , resampleBufferSize, true );
outputConvert = generateFlashMonoString;
}
else {
//STEREO Handling:
resampleBufferSize <<= 1 ;
resampled = getFloat32Flat(resampleBufferSize);
resampleControl = new Resampler(XAudioJSSampleRate, APISampleRate, 2 , resampleBufferSize, true );
outputConvert = generateFlashStereoString;
}
}
//Initialize WebKit Audio:
(function () {
if (!launchedContext) {
try {
// The following line was modified for benchmarking:
audioContextHandle = new GameBoyAudioContext(); //Create a system audio context.
}
catch (error) {
try {
audioContextHandle = new AudioContext(); //Create a system audio context.
}
catch (error) {
return ;
}
}
try {
audioSource = audioContextHandle.createBufferSource(); //We need to create a false input to get the chain started.
audioSource.loop = false ; //Keep this alive forever (Event handler will know when to ouput.)
XAudioJSSampleRate = webAudioActualSampleRate = audioContextHandle.sampleRate;
audioSource.buffer = audioContextHandle.createBuffer(1 , 1 , webAudioActualSampleRate); //Create a zero'd input buffer for the input to be valid.
audioNode = audioContextHandle.createJavaScriptNode(samplesPerCallback, 1 , 2 ); //Create 2 outputs and ignore the input buffer (Just copy buffer 1 over if mono)
audioNode.onaudioprocess = audioOutputEvent; //Connect the audio processing event to a handling function so we can manipulate output
audioSource.connect(audioNode); //Send and chain the input to the audio manipulation.
audioNode.connect(audioContextHandle.destination); //Send and chain the output of the audio manipulation to the system audio output.
audioSource.noteOn(0 ); //Start the loop!
}
catch (error) {
return ;
}
launchedContext = true ;
}
})();
// End of js/other/XAudioServer.js file.
// Start of js/other/resize.js file.
//JavaScript Image Resizer (c) 2012 - Grant Galitz
function Resize(widthOriginal, heightOriginal, targetWidth, targetHeight, blendAlpha, interpolationPass) {
this .widthOriginal = Math.abs(parseInt(widthOriginal) || 0 );
this .heightOriginal = Math.abs(parseInt(heightOriginal) || 0 );
this .targetWidth = Math.abs(parseInt(targetWidth) || 0 );
this .targetHeight = Math.abs(parseInt(targetHeight) || 0 );
this .colorChannels = (!!blendAlpha) ? 4 : 3 ;
this .interpolationPass = !!interpolationPass;
this .targetWidthMultipliedByChannels = this .targetWidth * this .colorChannels;
this .originalWidthMultipliedByChannels = this .widthOriginal * this .colorChannels;
this .originalHeightMultipliedByChannels = this .heightOriginal * this .colorChannels;
this .widthPassResultSize = this .targetWidthMultipliedByChannels * this .heightOriginal;
this .finalResultSize = this .targetWidthMultipliedByChannels * this .targetHeight;
this .initialize();
}
Resize.prototype.initialize = function () {
//Perform some checks:
if (this .widthOriginal > 0 && this .heightOriginal > 0 && this .targetWidth > 0 && this .targetHeight > 0 ) {
if (this .widthOriginal == this .targetWidth) {
//Bypass the width resizer pass:
this .resizeWidth = this .bypassResizer;
}
else {
//Setup the width resizer pass:
this .ratioWeightWidthPass = this .widthOriginal / this .targetWidth;
if (this .ratioWeightWidthPass < 1 && this .interpolationPass) {
this .initializeFirstPassBuffers(true );
this .resizeWidth = (this .colorChannels == 4 ) ? this .resizeWidthInterpolatedRGBA : this .resizeWidthInterpolatedRGB;
}
else {
this .initializeFirstPassBuffers(false );
this .resizeWidth = (this .colorChannels == 4 ) ? this .resizeWidthRGBA : this .resizeWidthRGB;
}
}
if (this .heightOriginal == this .targetHeight) {
//Bypass the height resizer pass:
this .resizeHeight = this .bypassResizer;
}
else {
//Setup the height resizer pass:
this .ratioWeightHeightPass = this .heightOriginal / this .targetHeight;
if (this .ratioWeightHeightPass < 1 && this .interpolationPass) {
this .initializeSecondPassBuffers(true );
this .resizeHeight = this .resizeHeightInterpolated;
}
else {
this .initializeSecondPassBuffers(false );
this .resizeHeight = (this .colorChannels == 4 ) ? this .resizeHeightRGBA : this .resizeHeightRGB;
}
}
}
else {
throw (new Error("Invalid settings specified for the resizer." ));
}
}
Resize.prototype.resizeWidthRGB = function (buffer) {
var ratioWeight = this .ratioWeightWidthPass;
var weight = 0 ;
var amountToNext = 0 ;
var actualPosition = 0 ;
var currentPosition = 0 ;
var line = 0 ;
var pixelOffset = 0 ;
var outputOffset = 0 ;
var nextLineOffsetOriginalWidth = this .originalWidthMultipliedByChannels - 2 ;
var nextLineOffsetTargetWidth = this .targetWidthMultipliedByChannels - 2 ;
var output = this .outputWidthWorkBench;
var outputBuffer = this .widthBuffer;
do {
for (line = 0 ; line < this .originalHeightMultipliedByChannels;) {
output[line++] = 0 ;
output[line++] = 0 ;
output[line++] = 0 ;
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
if (weight >= amountToNext) {
for (line = 0 , pixelOffset = actualPosition; line < this .originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
output[line++] += buffer[pixelOffset++] * amountToNext;
output[line++] += buffer[pixelOffset++] * amountToNext;
output[line++] += buffer[pixelOffset] * amountToNext;
}
currentPosition = actualPosition = actualPosition + 3 ;
weight -= amountToNext;
}
else {
for (line = 0 , pixelOffset = actualPosition; line < this .originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
output[line++] += buffer[pixelOffset++] * weight;
output[line++] += buffer[pixelOffset++] * weight;
output[line++] += buffer[pixelOffset] * weight;
}
currentPosition += weight;
break ;
}
} while (weight > 0 && actualPosition < this .originalWidthMultipliedByChannels);
for (line = 0 , pixelOffset = outputOffset; line < this .originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
outputBuffer[pixelOffset] = output[line++] / ratioWeight;
}
outputOffset += 3 ;
} while (outputOffset < this .targetWidthMultipliedByChannels);
return outputBuffer;
}
Resize.prototype.resizeWidthInterpolatedRGB = function (buffer) {
var ratioWeight = (this .widthOriginal - 1 ) / this .targetWidth;
var weight = 0 ;
var finalOffset = 0 ;
var pixelOffset = 0 ;
var outputBuffer = this .widthBuffer;
for (var targetPosition = 0 ; targetPosition < this .targetWidthMultipliedByChannels; targetPosition += 3 , weight += ratioWeight) {
//Calculate weightings:
secondWeight = weight % 1 ;
firstWeight = 1 - secondWeight;
//Interpolate:
for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * 3 ; finalOffset < this .widthPassResultSize; pixelOffset += this .originalWidthMultipliedByChannels, finalOffset += this .targetWidthMultipliedByChannels) {
outputBuffer[finalOffset] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 3 ] * secondWeight);
outputBuffer[finalOffset + 1 ] = (buffer[pixelOffset + 1 ] * firstWeight) + (buffer[pixelOffset + 4 ] * secondWeight);
outputBuffer[finalOffset + 2 ] = (buffer[pixelOffset + 2 ] * firstWeight) + (buffer[pixelOffset + 5 ] * secondWeight);
}
}
return outputBuffer;
}
Resize.prototype.resizeWidthRGBA = function (buffer) {
var ratioWeight = this .ratioWeightWidthPass;
var weight = 0 ;
var amountToNext = 0 ;
var actualPosition = 0 ;
var currentPosition = 0 ;
var line = 0 ;
var pixelOffset = 0 ;
var outputOffset = 0 ;
var nextLineOffsetOriginalWidth = this .originalWidthMultipliedByChannels - 3 ;
var nextLineOffsetTargetWidth = this .targetWidthMultipliedByChannels - 3 ;
var output = this .outputWidthWorkBench;
var outputBuffer = this .widthBuffer;
do {
for (line = 0 ; line < this .originalHeightMultipliedByChannels;) {
output[line++] = 0 ;
output[line++] = 0 ;
output[line++] = 0 ;
output[line++] = 0 ;
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
if (weight >= amountToNext) {
for (line = 0 , pixelOffset = actualPosition; line < this .originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
output[line++] += buffer[pixelOffset++] * amountToNext;
output[line++] += buffer[pixelOffset++] * amountToNext;
output[line++] += buffer[pixelOffset++] * amountToNext;
output[line++] += buffer[pixelOffset] * amountToNext;
}
currentPosition = actualPosition = actualPosition + 4 ;
weight -= amountToNext;
}
else {
for (line = 0 , pixelOffset = actualPosition; line < this .originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
output[line++] += buffer[pixelOffset++] * weight;
output[line++] += buffer[pixelOffset++] * weight;
output[line++] += buffer[pixelOffset++] * weight;
output[line++] += buffer[pixelOffset] * weight;
}
currentPosition += weight;
break ;
}
} while (weight > 0 && actualPosition < this .originalWidthMultipliedByChannels);
for (line = 0 , pixelOffset = outputOffset; line < this .originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
outputBuffer[pixelOffset] = output[line++] / ratioWeight;
}
outputOffset += 4 ;
} while (outputOffset < this .targetWidthMultipliedByChannels);
return outputBuffer;
}
Resize.prototype.resizeWidthInterpolatedRGBA = function (buffer) {
var ratioWeight = (this .widthOriginal - 1 ) / this .targetWidth;
var weight = 0 ;
var finalOffset = 0 ;
var pixelOffset = 0 ;
var outputBuffer = this .widthBuffer;
for (var targetPosition = 0 ; targetPosition < this .targetWidthMultipliedByChannels; targetPosition += 4 , weight += ratioWeight) {
//Calculate weightings:
secondWeight = weight % 1 ;
firstWeight = 1 - secondWeight;
//Interpolate:
for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * 4 ; finalOffset < this .widthPassResultSize; pixelOffset += this .originalWidthMultipliedByChannels, finalOffset += this .targetWidthMultipliedByChannels) {
outputBuffer[finalOffset] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 4 ] * secondWeight);
outputBuffer[finalOffset + 1 ] = (buffer[pixelOffset + 1 ] * firstWeight) + (buffer[pixelOffset + 5 ] * secondWeight);
outputBuffer[finalOffset + 2 ] = (buffer[pixelOffset + 2 ] * firstWeight) + (buffer[pixelOffset + 6 ] * secondWeight);
outputBuffer[finalOffset + 3 ] = (buffer[pixelOffset + 3 ] * firstWeight) + (buffer[pixelOffset + 7 ] * secondWeight);
}
}
return outputBuffer;
}
Resize.prototype.resizeHeightRGB = function (buffer) {
var ratioWeight = this .ratioWeightHeightPass;
var weight = 0 ;
var amountToNext = 0 ;
var actualPosition = 0 ;
var currentPosition = 0 ;
var pixelOffset = 0 ;
var outputOffset = 0 ;
var output = this .outputHeightWorkBench;
var outputBuffer = this .heightBuffer;
do {
for (pixelOffset = 0 ; pixelOffset < this .targetWidthMultipliedByChannels;) {
output[pixelOffset++] = 0 ;
output[pixelOffset++] = 0 ;
output[pixelOffset++] = 0 ;
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
if (weight >= amountToNext) {
for (pixelOffset = 0 ; pixelOffset < this .targetWidthMultipliedByChannels;) {
output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
}
currentPosition = actualPosition;
weight -= amountToNext;
}
else {
for (pixelOffset = 0 , amountToNext = actualPosition; pixelOffset < this .targetWidthMultipliedByChannels;) {
output[pixelOffset++] += buffer[amountToNext++] * weight;
output[pixelOffset++] += buffer[amountToNext++] * weight;
output[pixelOffset++] += buffer[amountToNext++] * weight;
}
currentPosition += weight;
break ;
}
} while (weight > 0 && actualPosition < this .widthPassResultSize);
for (pixelOffset = 0 ; pixelOffset < this .targetWidthMultipliedByChannels;) {
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
}
} while (outputOffset < this .finalResultSize);
return outputBuffer;
}
Resize.prototype.resizeHeightInterpolated = function (buffer) {
var ratioWeight = (this .heightOriginal - 1 ) / this .targetHeight;
var weight = 0 ;
var finalOffset = 0 ;
var pixelOffset = 0 ;
var pixelOffsetAccumulated = 0 ;
var pixelOffsetAccumulated2 = 0 ;
var outputBuffer = this .heightBuffer;
do {
//Calculate weightings:
secondWeight = weight % 1 ;
firstWeight = 1 - secondWeight;
//Interpolate:
pixelOffsetAccumulated = Math.floor(weight) * this .targetWidthMultipliedByChannels;
pixelOffsetAccumulated2 = pixelOffsetAccumulated + this .targetWidthMultipliedByChannels;
for (pixelOffset = 0 ; pixelOffset < this .targetWidthMultipliedByChannels; ++pixelOffset) {
outputBuffer[finalOffset++] = (buffer[pixelOffsetAccumulated + pixelOffset] * firstWeight) + (buffer[pixelOffsetAccumulated2 + pixelOffset] * secondWeight);
}
weight += ratioWeight;
} while (finalOffset < this .finalResultSize);
return outputBuffer;
}
Resize.prototype.resizeHeightRGBA = function (buffer) {
var ratioWeight = this .ratioWeightHeightPass;
var weight = 0 ;
var amountToNext = 0 ;
var actualPosition = 0 ;
var currentPosition = 0 ;
var pixelOffset = 0 ;
var outputOffset = 0 ;
var output = this .outputHeightWorkBench;
var outputBuffer = this .heightBuffer;
do {
for (pixelOffset = 0 ; pixelOffset < this .targetWidthMultipliedByChannels;) {
output[pixelOffset++] = 0 ;
output[pixelOffset++] = 0 ;
output[pixelOffset++] = 0 ;
output[pixelOffset++] = 0 ;
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
if (weight >= amountToNext) {
for (pixelOffset = 0 ; pixelOffset < this .targetWidthMultipliedByChannels;) {
output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
}
currentPosition = actualPosition;
weight -= amountToNext;
}
else {
for (pixelOffset = 0 , amountToNext = actualPosition; pixelOffset < this .targetWidthMultipliedByChannels;) {
output[pixelOffset++] += buffer[amountToNext++] * weight;
output[pixelOffset++] += buffer[amountToNext++] * weight;
output[pixelOffset++] += buffer[amountToNext++] * weight;
output[pixelOffset++] += buffer[amountToNext++] * weight;
}
currentPosition += weight;
break ;
}
} while (weight > 0 && actualPosition < this .widthPassResultSize);
for (pixelOffset = 0 ; pixelOffset < this .targetWidthMultipliedByChannels;) {
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
}
} while (outputOffset < this .finalResultSize);
return outputBuffer;
}
Resize.prototype.resizeHeightInterpolatedRGBA = function (buffer) {
var ratioWeight = (this .heightOriginal - 1 ) / this .targetHeight;
var weight = 0 ;
var finalOffset = 0 ;
var pixelOffset = 0 ;
var outputBuffer = this .heightBuffer;
while (pixelOffset < this .finalResultSize) {
//Calculate weightings:
secondWeight = weight % 1 ;
firstWeight = 1 - secondWeight;
//Interpolate:
for (pixelOffset = Math.floor(weight) * 4 ; pixelOffset < this .targetWidthMultipliedByChannels; pixelOffset += 4 ) {
outputBuffer[finalOffset++] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 4 ] * secondWeight);
outputBuffer[finalOffset++] = (buffer[pixelOffset + 1 ] * firstWeight) + (buffer[pixelOffset + 5 ] * secondWeight);
outputBuffer[finalOffset++] = (buffer[pixelOffset + 2 ] * firstWeight) + (buffer[pixelOffset + 6 ] * secondWeight);
outputBuffer[finalOffset++] = (buffer[pixelOffset + 3 ] * firstWeight) + (buffer[pixelOffset + 7 ] * secondWeight);
}
weight += ratioWeight;
}
return outputBuffer;
}
Resize.prototype.resize = function (buffer) {
return this .resizeHeight(this .resizeWidth(buffer));
}
Resize.prototype.bypassResizer = function (buffer) {
//Just return the buffer passsed:
return buffer;
}
Resize.prototype.initializeFirstPassBuffers = function (BILINEARAlgo) {
//Initialize the internal width pass buffers:
this .widthBuffer = this .generateFloatBuffer(this .widthPassResultSize);
if (!BILINEARAlgo) {
this .outputWidthWorkBench = this .generateFloatBuffer(this .originalHeightMultipliedByChannels);
}
}
Resize.prototype.initializeSecondPassBuffers = function (BILINEARAlgo) {
//Initialize the internal height pass buffers:
this .heightBuffer = this .generateUint8Buffer(this .finalResultSize);
if (!BILINEARAlgo) {
this .outputHeightWorkBench = this .generateFloatBuffer(this .targetWidthMultipliedByChannels);
}
}
Resize.prototype.generateFloatBuffer = function (bufferLength) {
//Generate a float32 typed array buffer:
try {
return new Float32Array(bufferLength);
}
catch (error) {
return [];
}
}
Resize.prototype.generateUint8Buffer = function (bufferLength) {
//Generate a uint8 typed array buffer:
try {
return this .checkForOperaMathBug(new Uint8Array(bufferLength));
}
catch (error) {
return [];
}
}
Resize.prototype.checkForOperaMathBug = function (typedArray) {
typedArray[0 ] = -1 ;
typedArray[0 ] >>= 0 ;
if (typedArray[0 ] != 0 xFF) {
return [];
}
else {
return typedArray;
}
}
// End of js/other/resize.js file.
// Remaining files are in gbemu-part2.js, since they run in strict mode.
Messung V0.5 in Prozent C=93 H=87 G=89
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland