2018-06-16 00:22:31 +02:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
(function(){
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
var scope;
|
|
|
|
if ((typeof exports) !== "undefined"){
|
|
|
|
scope = exports;
|
|
|
|
}
|
|
|
|
else {
|
2019-03-12 22:24:23 +01:00
|
|
|
scope = require.register("./modifiedAudioAPI", {});
|
2018-06-16 00:22:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const logging = require("./logging");
|
2018-06-30 00:34:20 +02:00
|
|
|
const {sha256String: hashing} = require("./hash");
|
2018-10-23 08:26:23 +02:00
|
|
|
const {checkerWrapper} = require("./modifiedAPIFunctions");
|
2018-06-16 00:22:31 +02:00
|
|
|
|
|
|
|
var randomSupply = null;
|
|
|
|
|
2018-06-21 00:20:41 +02:00
|
|
|
const getAudioFakeRate = function(){
|
|
|
|
const audioFakeRate = {
|
|
|
|
"1": function(array){return 1;},
|
|
|
|
"10": function(array){return 10;},
|
|
|
|
"100": function(array){return 100;},
|
|
|
|
"1000": function(array){return 1000;},
|
|
|
|
"0.1%": function(array){return array.length / 1000;},
|
|
|
|
"1%": function(array){return array.length / 100;},
|
2018-07-04 23:59:27 +02:00
|
|
|
"10%": function(array){return array.length / 10;},
|
|
|
|
"100%": function(array){return array.length;},
|
2018-06-21 00:20:41 +02:00
|
|
|
};
|
|
|
|
return function getAudioFakeRate(array, prefs){
|
|
|
|
var func = audioFakeRate[prefs("audioFakeRate")];
|
|
|
|
if (typeof func === "function"){
|
|
|
|
return func(array);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}();
|
|
|
|
const getAudioNoiseLevel = function(){
|
|
|
|
const audioNoiseLevel = {
|
|
|
|
"minimal": 0.0001,
|
|
|
|
"low": 0.0005,
|
|
|
|
"medium": 0.001,
|
|
|
|
"high": 0.005,
|
|
|
|
"maximal": 0.01
|
|
|
|
};
|
|
|
|
return function getAudioNoiseLevel(prefs){
|
|
|
|
return audioNoiseLevel[prefs("audioNoiseLevel")] || 0.0001;
|
|
|
|
};
|
|
|
|
}();
|
|
|
|
function forEachFixedIndex(prefs, callback){
|
|
|
|
if (prefs("audioUseFixedIndices")){
|
|
|
|
prefs("audioFixedIndices")
|
|
|
|
.split(",")
|
|
|
|
.map(function(str){
|
|
|
|
return parseInt(str, 10);
|
|
|
|
}).filter(function(num){
|
|
|
|
return !isNaN(num);
|
|
|
|
}).filter(function(num, i, array){
|
|
|
|
return array.indexOf(num) === i;
|
|
|
|
}).forEach(callback);
|
2018-06-16 00:22:31 +02:00
|
|
|
}
|
|
|
|
}
|
2018-06-21 00:20:41 +02:00
|
|
|
|
|
|
|
function forEachIndex(array, prefs, callback){
|
2018-06-30 23:34:59 +02:00
|
|
|
var length = array.length;
|
2018-06-21 00:20:41 +02:00
|
|
|
var rate = getAudioFakeRate(array, prefs);
|
|
|
|
var start = 0;
|
|
|
|
forEachFixedIndex(prefs, function(index){
|
|
|
|
callback(index, start);
|
|
|
|
start += 1;
|
|
|
|
});
|
2018-06-30 23:34:59 +02:00
|
|
|
if (start < rate){
|
|
|
|
var delta = Math.floor(length / (rate - start));
|
|
|
|
var indexRng = randomSupply.getIndexRng(1, length - delta * (rate - start - 1), window);
|
|
|
|
var offset = indexRng(0);
|
|
|
|
for (var i = start; i < rate; i += 1){
|
|
|
|
callback(offset, i);
|
|
|
|
offset += delta;
|
|
|
|
}
|
2018-06-21 00:20:41 +02:00
|
|
|
}
|
2018-06-16 00:22:31 +02:00
|
|
|
}
|
|
|
|
|
2018-06-30 00:34:20 +02:00
|
|
|
const floatCache = Object.create(null);
|
|
|
|
const intCache = Object.create(null);
|
|
|
|
|
2019-02-10 02:55:45 +01:00
|
|
|
function arrayHasAnyNonZero(array){
|
|
|
|
for (var i = 0, l = array.length; i < l; i += 1){
|
|
|
|
if (array[i]){
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-16 00:22:31 +02:00
|
|
|
function fakeFloat32Array(array, window, prefs){
|
2019-02-10 02:55:45 +01:00
|
|
|
if (arrayHasAnyNonZero(array)){
|
2018-06-30 00:34:20 +02:00
|
|
|
let cached = false;
|
|
|
|
let hash;
|
|
|
|
if (prefs("useAudioCache")){
|
|
|
|
hash = hashing(array);
|
|
|
|
cached = floatCache[hash];
|
|
|
|
}
|
|
|
|
if (!cached){
|
|
|
|
var rate = getAudioFakeRate(array, prefs);
|
|
|
|
var noiseLevel = getAudioNoiseLevel(prefs);
|
|
|
|
var rng = randomSupply.getRng(rate, window);
|
|
|
|
forEachIndex(array, prefs, function(index, i){
|
|
|
|
let value;
|
|
|
|
if (array[index] !== 0){
|
|
|
|
value = array[index] * (1 + (rng(i) / 0xffffffff - 0.5) * noiseLevel);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
value = Number.EPSILON * (rng(i) / 0xffffffff - 0.5) * noiseLevel;
|
|
|
|
}
|
|
|
|
array[index] = value;
|
|
|
|
});
|
|
|
|
if (prefs("useAudioCache")){
|
2019-05-22 10:06:11 +02:00
|
|
|
floatCache[hash] = new array.constructor(array);
|
|
|
|
floatCache[hashing(array)] = floatCache[hash];
|
2018-06-29 23:27:20 +02:00
|
|
|
}
|
2018-06-30 00:34:20 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
array.set(cached);
|
|
|
|
}
|
2018-06-16 00:22:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
function fakeUint8Array(array, window, prefs){
|
2019-02-10 02:55:45 +01:00
|
|
|
if (arrayHasAnyNonZero(array)){
|
2018-06-30 00:34:20 +02:00
|
|
|
let cached = false;
|
|
|
|
let hash;
|
|
|
|
if (prefs("useAudioCache")){
|
|
|
|
hash = hashing(array);
|
|
|
|
cached = intCache[hash];
|
|
|
|
}
|
|
|
|
if (!cached){
|
|
|
|
var rate = getAudioFakeRate(array, prefs);
|
|
|
|
var rng = randomSupply.getValueRng(rate, window);
|
2018-09-18 21:52:15 +02:00
|
|
|
forEachIndex(array, prefs, function(index, i){
|
2018-06-30 00:34:20 +02:00
|
|
|
array[index] = rng(array[index], i);
|
|
|
|
});
|
|
|
|
if (prefs("useAudioCache")){
|
2019-05-22 10:06:11 +02:00
|
|
|
intCache[hash] = new array.constructor(array);
|
|
|
|
intCache[hashing(array)] = intCache[hash];
|
2018-06-30 00:34:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
array.set(cached);
|
|
|
|
}
|
2018-06-16 00:22:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
scope.setRandomSupply = function(supply){
|
|
|
|
randomSupply = supply;
|
|
|
|
};
|
|
|
|
|
2018-09-06 17:24:05 +02:00
|
|
|
function getStatus(obj, status, prefs){
|
2018-06-16 00:22:31 +02:00
|
|
|
status = Object.create(status);
|
2018-10-23 08:26:23 +02:00
|
|
|
status.active = prefs("protectAudio", status.url);
|
2018-06-16 00:22:31 +02:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2019-02-07 20:12:12 +01:00
|
|
|
const getChannelDataAlreadyFakedArrays = new WeakMap();
|
2018-06-16 00:22:31 +02:00
|
|
|
// changed functions and their fakes
|
|
|
|
scope.changedFunctions = {
|
|
|
|
getFloatFrequencyData: {
|
|
|
|
object: ["AnalyserNode"],
|
2018-07-13 16:58:13 +02:00
|
|
|
fakeGenerator: function(checker){
|
2018-06-16 00:22:31 +02:00
|
|
|
return function getFloatFrequencyData(array){
|
2018-07-13 16:58:13 +02:00
|
|
|
return checkerWrapper(checker, this, arguments, function(args, check){
|
|
|
|
var {prefs, notify, window, original} = check;
|
2018-09-16 12:09:48 +02:00
|
|
|
notify("fakedAudioReadout");
|
2018-07-13 16:58:13 +02:00
|
|
|
var ret = original.apply(this, window.Array.from(args));
|
|
|
|
fakeFloat32Array(array, window, prefs);
|
|
|
|
return ret;
|
|
|
|
});
|
2018-06-16 00:22:31 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getByteFrequencyData: {
|
|
|
|
object: ["AnalyserNode"],
|
2018-07-13 16:58:13 +02:00
|
|
|
fakeGenerator: function(checker){
|
2018-06-16 00:22:31 +02:00
|
|
|
return function getByteFrequencyData(array){
|
2018-07-13 16:58:13 +02:00
|
|
|
return checkerWrapper(checker, this, arguments, function(args, check){
|
|
|
|
var {prefs, notify, window, original} = check;
|
2018-09-16 12:09:48 +02:00
|
|
|
notify("fakedAudioReadout");
|
2018-07-13 16:58:13 +02:00
|
|
|
var ret = original.apply(this, window.Array.from(args));
|
|
|
|
fakeUint8Array(array, window, prefs);
|
|
|
|
return ret;
|
|
|
|
});
|
2018-06-16 00:22:31 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getFloatTimeDomainData: {
|
|
|
|
object: ["AnalyserNode"],
|
2018-07-13 16:58:13 +02:00
|
|
|
fakeGenerator: function(checker){
|
2018-06-16 00:22:31 +02:00
|
|
|
return function getFloatTimeDomainData(array){
|
2018-07-13 16:58:13 +02:00
|
|
|
return checkerWrapper(checker, this, arguments, function(args, check){
|
|
|
|
var {prefs, notify, window, original} = check;
|
2018-09-16 12:09:48 +02:00
|
|
|
notify("fakedAudioReadout");
|
2018-07-13 16:58:13 +02:00
|
|
|
var ret = original.apply(this, window.Array.from(args));
|
|
|
|
fakeFloat32Array(array, window, prefs);
|
|
|
|
return ret;
|
|
|
|
});
|
2018-06-16 00:22:31 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getByteTimeDomainData: {
|
|
|
|
object: ["AnalyserNode"],
|
2018-07-13 16:58:13 +02:00
|
|
|
fakeGenerator: function(checker){
|
2018-06-16 00:22:31 +02:00
|
|
|
return function getByteTimeDomainData(array){
|
2018-07-13 16:58:13 +02:00
|
|
|
return checkerWrapper(checker, this, arguments, function(args, check){
|
|
|
|
var {prefs, notify, window, original} = check;
|
2018-09-16 12:09:48 +02:00
|
|
|
notify("fakedAudioReadout");
|
2018-07-13 16:58:13 +02:00
|
|
|
var ret = original.apply(this, window.Array.from(args));
|
|
|
|
fakeUint8Array(array, window, prefs);
|
|
|
|
return ret;
|
|
|
|
});
|
2018-06-16 00:22:31 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getChannelData: {
|
|
|
|
object: ["AudioBuffer"],
|
2018-07-13 16:58:13 +02:00
|
|
|
fakeGenerator: function(checker){
|
2018-06-16 00:22:31 +02:00
|
|
|
return function getChannelData(channel){
|
2018-07-13 16:58:13 +02:00
|
|
|
return checkerWrapper(checker, this, arguments, function(args, check){
|
|
|
|
var {prefs, notify, window, original} = check;
|
|
|
|
var ret = original.apply(this, window.Array.from(args));
|
2019-02-07 20:12:12 +01:00
|
|
|
if (!getChannelDataAlreadyFakedArrays.get(ret)){
|
|
|
|
notify("fakedAudioReadout");
|
|
|
|
fakeFloat32Array(ret, window, prefs);
|
|
|
|
getChannelDataAlreadyFakedArrays.set(ret, true);
|
|
|
|
}
|
2018-07-13 16:58:13 +02:00
|
|
|
return ret;
|
|
|
|
});
|
2018-06-16 00:22:31 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
copyFromChannel: {
|
|
|
|
object: ["AudioBuffer"],
|
2018-07-13 16:58:13 +02:00
|
|
|
fakeGenerator: function(checker){
|
2018-06-16 00:22:31 +02:00
|
|
|
return function copyFromChannel(destination, channelNumber, startInChannel){
|
2018-07-13 16:58:13 +02:00
|
|
|
return checkerWrapper(checker, this, arguments, function(args, check){
|
|
|
|
var {prefs, notify, window, original} = check;
|
2019-02-10 02:55:45 +01:00
|
|
|
var channelData = this.getChannelData(channelNumber);
|
|
|
|
if (!getChannelDataAlreadyFakedArrays.get(channelData)){
|
|
|
|
notify("fakedAudioReadout");
|
|
|
|
fakeFloat32Array(channelData, window, prefs);
|
|
|
|
getChannelDataAlreadyFakedArrays.set(channelData, true);
|
|
|
|
}
|
2018-07-13 16:58:13 +02:00
|
|
|
var ret = original.apply(this, window.Array.from(args));
|
|
|
|
return ret;
|
|
|
|
});
|
2018-06-16 00:22:31 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getFrequencyResponse: {
|
|
|
|
object: ["BiquadFilterNode", "IIRFilterNode"],
|
2018-07-13 16:58:13 +02:00
|
|
|
fakeGenerator: function(checker){
|
2018-06-16 00:22:31 +02:00
|
|
|
return function getFrequencyResponse(frequencyArray, magResponseOutput, phaseResponseOutput){
|
2018-07-13 16:58:13 +02:00
|
|
|
return checkerWrapper(checker, this, arguments, function(args, check){
|
|
|
|
var {prefs, notify, window, original} = check;
|
2018-09-16 12:09:48 +02:00
|
|
|
notify("fakedAudioReadout");
|
2018-07-13 16:58:13 +02:00
|
|
|
var ret = original.apply(this, window.Array.from(args));
|
|
|
|
fakeFloat32Array(magResponseOutput, window, prefs);
|
|
|
|
fakeFloat32Array(phaseResponseOutput, window, prefs);
|
|
|
|
return ret;
|
|
|
|
});
|
2018-06-16 00:22:31 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
2018-06-21 00:09:07 +02:00
|
|
|
Object.keys(scope.changedFunctions).forEach(function(key){
|
|
|
|
scope.changedFunctions[key].type = "readout";
|
|
|
|
scope.changedFunctions[key].getStatus = getStatus;
|
|
|
|
scope.changedFunctions[key].api = "audio";
|
|
|
|
});
|
2018-06-16 00:22:31 +02:00
|
|
|
}());
|