Backed out changeset e7215fed014f (bug 1025864) for frequent OSX debug crashes.

This commit is contained in:
Ryan VanderMeulen
2014-08-27 14:12:31 -04:00
parent d40727fe5d
commit 5a14341b43
11 changed files with 53 additions and 472 deletions

View File

@@ -4,8 +4,6 @@
"use strict";
let { utils: Cu, interfaces: Ci } = Components;
addMessageListener("devtools:test:history", function ({ data }) {
content.history[data.direction]();
});
@@ -18,11 +16,3 @@ addMessageListener("devtools:test:reload", function ({ data }) {
data = data || {};
content.location.reload(data.forceget);
});
addMessageListener("devtools:test:forceCC", function () {
let DOMWindowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
DOMWindowUtils.cycleCollect();
DOMWindowUtils.garbageCollect();
DOMWindowUtils.garbageCollect();
});

View File

@@ -10,7 +10,6 @@ support-files =
doc_connect-toggle.html
doc_connect-param.html
doc_connect-multi-param.html
doc_change-param.html
440hz_sine.ogg
head.js
@@ -20,10 +19,6 @@ support-files =
[browser_audionode-actor-get-set-param.js]
[browser_audionode-actor-get-type.js]
[browser_audionode-actor-is-source.js]
[browser_webaudio-actor-change-params-01.js]
[browser_webaudio-actor-change-params-02.js]
[browser_webaudio-actor-change-params-03.js]
[browser_webaudio-actor-connect-param.js]
[browser_webaudio-actor-destroy-node.js]
[browser_webaudio-actor-simple.js]
@@ -37,7 +32,6 @@ support-files =
[browser_wa_graph-click.js]
[browser_wa_graph-markers.js]
skip-if = (os == 'mac' && debug) # bug 1035820
[browser_wa_graph-render-01.js]
[browser_wa_graph-render-02.js]
[browser_wa_graph-render-03.js]
@@ -49,7 +43,6 @@ skip-if = (os == 'mac' && debug) # bug 1035820
[browser_wa_inspector-toggle.js]
[browser_wa_properties-view.js]
[browser_wa_properties-view-change-params.js]
[browser_wa_properties-view-edit-01.js]
skip-if = true # bug 1010423
[browser_wa_properties-view-edit-02.js]

View File

@@ -1,46 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that params view correctly updates changed parameters
* when source code updates them, as well as CHANGE_PARAM events.
*/
function spawnTest() {
let [target, debuggee, panel] = yield initWebAudioEditor(CHANGE_PARAM_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, WebAudioInspectorView } = panelWin;
let gVars = WebAudioInspectorView._propsView;
// Set parameter polling to 20ms for tests
panelWin.PARAM_POLLING_FREQUENCY = 20;
let started = once(gFront, "start-context");
reload(target);
let [actors] = yield Promise.all([
getN(gFront, "create-node", 3),
waitForGraphRendered(panelWin, 3, 0)
]);
let oscId = actors[1].actorID;
click(panelWin, findGraphNode(panelWin, oscId));
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
// Yield twice so we get a diff
yield once(panelWin, EVENTS.CHANGE_PARAM);
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
is(args.actorID, oscId, "EVENTS.CHANGE_PARAM has correct `actorID`");
ok(args.oldValue < args.newValue, "EVENTS.CHANGE_PARAM has correct `newValue` and `oldValue`");
is(args.param, "detune", "EVENTS.CHANGE_PARAM has correct `param`");
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
checkVariableView(gVars, 0, { "detune": args.newValue }, "`detune` parameter updated.");
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
checkVariableView(gVars, 0, { "detune": args.newValue }, "`detune` parameter updated.");
yield teardown(panel);
finish();
}

View File

@@ -1,46 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test WebAudioActor `change-param` events and front.[en|dis]ableChangeParamEvents
*/
function spawnTest () {
let [target, debuggee, front] = yield initBackend(CHANGE_PARAM_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
getN(front, "create-node", 3)
]);
let osc = nodes[1];
let eventCount = 0;
yield front.enableChangeParamEvents(osc, 20);
front.on("change-param", onChangeParam);
yield getN(front, "change-param", 3);
yield front.disableChangeParamEvents();
let currEventCount = eventCount;
// Be flexible here incase we get an extra counter before the listener is turned off
ok(eventCount >= 3, "Calling `enableChangeParamEvents` should allow front to emit `change-param`.");
yield wait(100);
ok((eventCount - currEventCount) <= 2, "Calling `disableChangeParamEvents` should turn off the listener.");
front.off("change-param", onChangeParam);
yield removeTab(target.tab);
finish();
function onChangeParam ({ newValue, oldValue, param, actorID }) {
is(actorID, osc.actorID, "correct `actorID` in `change-param`.");
is(param, "detune", "correct `param` property in `change-param`.");
ok(newValue > oldValue,
"correct `newValue` (" + newValue + ") and `oldValue` (" + oldValue + ") in `change-param`");
eventCount++;
}
}

View File

@@ -1,36 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that listening to param change polling does not break when the AudioNode is collected.
*/
function spawnTest () {
let [target, debuggee, front] = yield initBackend(DESTROY_NODES_URL);
let waitUntilDestroyed = getN(front, "destroy-node", 10);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
getN(front, "create-node", 13)
]);
let bufferNode = nodes[6];
yield front.enableChangeParamEvents(bufferNode, 20);
front.on("change-param", onChangeParam);
forceCC();
yield waitUntilDestroyed;
yield wait(50);
front.off("change-param", onChangeParam);
ok(true, "listening to `change-param` on a dead node doesn't throw.");
yield removeTab(target.tab);
finish();
function onChangeParam (args) {
ok(false, "`change-param` should not be emitted on a node that hasn't changed params or is dead.");
}
}

View File

@@ -1,32 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test WebAudioActor `change-param` events on special types.
*/
function spawnTest () {
let [target, debuggee, front] = yield initBackend(CHANGE_PARAM_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
getN(front, "create-node", 3)
]);
let shaper = nodes[2];
let eventCount = 0;
yield front.enableChangeParamEvents(shaper, 20);
let onChange = once(front, "change-param");
shaper.setParam("curve", null);
let { newValue, oldValue } = yield onChange;
is(oldValue.type, "object", "`oldValue` should be an object.");
is(oldValue.class, "Float32Array", "`oldValue` should be of class Float32Array.");
is(newValue.type, "null", "`newValue` should be null.");
yield removeTab(target.tab);
finish();
}

View File

@@ -1,25 +0,0 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Web Audio Editor test page</title>
</head>
<body>
<script type="text/javascript;version=1.8">
"use strict";
let ctx = new AudioContext();
let osc = ctx.createOscillator();
let shaperNode = ctx.createWaveShaper();
let detuneVal = 0;
shaperNode.curve = new Float32Array(65536);
setInterval(() => osc.detune.value = ++detuneVal, 10);
</script>
</body>
</html>

View File

@@ -19,9 +19,7 @@ let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.j
let { WebAudioFront } = devtools.require("devtools/server/actors/webaudio");
let TargetFactory = devtools.TargetFactory;
let mm = null;
const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js";
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/test/";
const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
@@ -32,7 +30,6 @@ const DESTROY_NODES_URL = EXAMPLE_URL + "doc_destroy-nodes.html";
const CONNECT_TOGGLE_URL = EXAMPLE_URL + "doc_connect-toggle.html";
const CONNECT_PARAM_URL = EXAMPLE_URL + "doc_connect-param.html";
const CONNECT_MULTI_PARAM_URL = EXAMPLE_URL + "doc_connect-multi-param.html";
const CHANGE_PARAM_URL = EXAMPLE_URL + "doc_change-param.html";
// All tests are asynchronous.
waitForExplicitFinish();
@@ -136,8 +133,6 @@ function initBackend(aUrl) {
yield target.makeRemote();
let front = new WebAudioFront(target.client, target.form);
loadFrameScripts();
return [target, debuggee, front];
});
}
@@ -155,8 +150,6 @@ function initWebAudioEditor(aUrl) {
Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
let toolbox = yield gDevTools.showToolbox(target, "webaudioeditor");
let panel = toolbox.getCurrentPanel();
loadFrameScripts();
return [target, debuggee, panel];
});
}
@@ -394,12 +387,9 @@ function countGraphObjects (win) {
* Forces cycle collection and GC, used in AudioNode destruction tests.
*/
function forceCC () {
mm.sendAsyncMessage("devtools:test:forceCC");
}
function loadFrameScripts () {
mm = gBrowser.selectedBrowser.messageManager;
mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
SpecialPowers.DOMWindowUtils.cycleCollect();
SpecialPowers.DOMWindowUtils.garbageCollect();
SpecialPowers.DOMWindowUtils.garbageCollect();
}
/**

View File

@@ -20,9 +20,8 @@ const STRINGS_URI = "chrome://browser/locale/devtools/webaudioeditor.properties"
const L10N = new ViewHelpers.L10N(STRINGS_URI);
const Telemetry = require("devtools/shared/telemetry");
const telemetry = new Telemetry();
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let PARAM_POLLING_FREQUENCY = 1000;
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
@@ -174,8 +173,6 @@ let WebAudioEditorController = {
telemetry.toolOpened("webaudioeditor");
this._onTabNavigated = this._onTabNavigated.bind(this);
this._onThemeChange = this._onThemeChange.bind(this);
this._onSelectNode = this._onSelectNode.bind(this);
this._onChangeParam = this._onChangeParam.bind(this);
gTarget.on("will-navigate", this._onTabNavigated);
gTarget.on("navigate", this._onTabNavigated);
gFront.on("start-context", this._onStartContext);
@@ -197,15 +194,12 @@ let WebAudioEditorController = {
window.on(EVENTS.DISCONNECT_NODE, this._onUpdatedContext);
window.on(EVENTS.DESTROY_NODE, this._onUpdatedContext);
window.on(EVENTS.CONNECT_PARAM, this._onUpdatedContext);
// Set up a controller for managing parameter changes per audio node
window.on(EVENTS.UI_SELECT_NODE, this._onSelectNode);
},
/**
* Remove events emitted by the current tab target.
*/
destroy: Task.async(function* () {
destroy: function() {
telemetry.toolClosed("webaudioeditor");
gTarget.off("will-navigate", this._onTabNavigated);
gTarget.off("navigate", this._onTabNavigated);
@@ -221,11 +215,8 @@ let WebAudioEditorController = {
window.off(EVENTS.DISCONNECT_NODE, this._onUpdatedContext);
window.off(EVENTS.DESTROY_NODE, this._onUpdatedContext);
window.off(EVENTS.CONNECT_PARAM, this._onUpdatedContext);
window.off(EVENTS.UI_SELECT_NODE, this._onSelectNode);
gDevTools.off("pref-changed", this._onThemeChange);
yield gFront.disableChangeParamEvents();
}),
},
/**
* Called when page is reloaded to show the reload notice and waiting
@@ -354,21 +345,9 @@ let WebAudioEditorController = {
/**
* Called when a node param is changed.
*/
_onChangeParam: function (args) {
window.emit(EVENTS.CHANGE_PARAM, args);
},
/**
* Called on UI_SELECT_NODE, used to manage
* `change-param` events on that node.
*/
_onSelectNode: function (_, id) {
let node = getViewNodeById(id);
if (node && node.actor) {
gFront.enableChangeParamEvents(node.actor, PARAM_POLLING_FREQUENCY);
}
},
_onChangeParam: function({ actor, param, value }) {
window.emit(EVENTS.CHANGE_PARAM, getViewNodeByActor(actor), param, value);
}
};
/**

View File

@@ -377,7 +377,6 @@ let WebAudioInspectorView = {
this._onNodeSelect = this._onNodeSelect.bind(this);
this._onTogglePaneClick = this._onTogglePaneClick.bind(this);
this._onDestroyNode = this._onDestroyNode.bind(this);
this._onChangeParam = this._onChangeParam.bind(this);
this._inspectorPaneToggleButton.addEventListener("mousedown", this._onTogglePaneClick, false);
this._propsView = new VariablesView($("#properties-tabpanel-content"), GENERIC_VARIABLES_VIEW_SETTINGS);
@@ -385,7 +384,6 @@ let WebAudioInspectorView = {
window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
window.on(EVENTS.DESTROY_NODE, this._onDestroyNode);
window.on(EVENTS.CHANGE_PARAM, this._onChangeParam);
},
/**
@@ -395,7 +393,6 @@ let WebAudioInspectorView = {
this._inspectorPaneToggleButton.removeEventListener("mousedown", this._onTogglePaneClick);
window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
window.off(EVENTS.DESTROY_NODE, this._onDestroyNode);
window.off(EVENTS.CHANGE_PARAM, this._onChangeParam);
this._inspectorPane = null;
this._inspectorPaneToggleButton = null;
@@ -615,22 +612,7 @@ let WebAudioInspectorView = {
if (this._currentNode && this._currentNode.id === id) {
this.setCurrentAudioNode(null);
}
},
/**
* Called when `CHANGE_PARAM` is fired. We should ensure that this event is
* for the same node that is currently selected. We check the existence
* of each part of the scope to make sure that if this event was fired
* during a VariablesView rebuild, then we just ignore it.
*/
_onChangeParam: function (_, { param, newValue, oldValue, actorID }) {
if (!this._currentNode || this._currentNode.actor.actorID !== actorID) return;
let scope = this._getAudioPropertiesScope();
if (!scope) return;
let property = scope.get(param);
if (!property) return;
property.setGrip(newValue);
},
}
};
/**

View File

@@ -4,14 +4,16 @@
"use strict";
const {Cc, Ci, Cu, Cr} = require("chrome");
const Services = require("Services");
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
const events = require("sdk/event/core");
const { on: systemOn, off: systemOff } = require("sdk/system/events");
const { setTimeout, clearTimeout } = require("sdk/timers");
const protocol = require("devtools/server/protocol");
const { CallWatcherActor, CallWatcherFront } = require("devtools/server/actors/call-watcher");
const { ThreadActor } = require("devtools/server/actors/script");
const { on, once, off, emit } = events;
const { method, Arg, Option, RetVal } = protocol;
@@ -25,10 +27,6 @@ exports.unregister = function(handle) {
handle.removeGlobalActor(WebAudioActor);
};
// In milliseconds, how often should AudioNodes poll to see
// if an AudioParam's value has changed to emit to the client.
const PARAM_POLLING_FREQUENCY = 1000;
const AUDIO_GLOBALS = [
"AudioContext", "AudioNode"
];
@@ -148,10 +146,6 @@ let AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass({
}
},
destroy: function(conn) {
protocol.Actor.prototype.destroy.call(this, conn);
},
/**
* Returns the name of the audio type.
* Examples: "OscillatorNode", "MediaElementAudioSourceNode"
@@ -193,7 +187,6 @@ let AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass({
node[param].value = value;
else
node[param] = value;
return undefined;
} catch (e) {
return constructError(e);
@@ -228,7 +221,14 @@ let AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass({
// AudioBuffer or Float32Array references and the like,
// so this just formats the value to be displayed in the VariablesView,
// without using real grips and managing via actor pools.
return createGrip(value);
let grip;
try {
grip = ThreadActor.prototype.createValueGrip(value);
}
catch (e) {
grip = createObjectGrip(value);
}
return grip;
}, {
request: {
param: Arg(0, "string")
@@ -252,27 +252,16 @@ let AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass({
}),
/**
* Get an array of objects each containing a `param`, `value` and `flags` property,
* corresponding to a property name and current value of the audio node, and any
* associated flags as defined by NODE_PROPERTIES.
* Get an array of objects each containing a `param` and `value` property,
* corresponding to a property name and current value of the audio node.
*/
getParams: method(function () {
getParams: method(function (param) {
let props = Object.keys(NODE_PROPERTIES[this.type]);
return props.map(prop =>
({ param: prop, value: this.getParam(prop), flags: this.getParamFlags(prop) }));
}, {
response: { params: RetVal("json") }
}),
/**
* Returns a boolean indicating whether or not
* the underlying AudioNode has been collected yet or not.
*
* @return Boolean
*/
isAlive: function () {
return !!this.node.get();
}
})
});
/**
@@ -418,7 +407,6 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
this.tabActor = null;
this._initialized = false;
off(this._callWatcher._contentObserver, "global-destroyed", this._onGlobalDestroyed);
this.disableChangeParamEvents();
this._nativeToActorID = null;
this._callWatcher.eraseRecording();
this._callWatcher.finalize();
@@ -427,59 +415,6 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
oneway: true
}),
/**
* Takes an AudioNodeActor and a duration specifying how often
* should the node's parameters be polled to detect changes. Emits
* `change-param` when a change is found.
*
* Currently, only one AudioNodeActor can be listened to at a time.
*
* `wait` is used in tests to specify the poll timer.
*/
enableChangeParamEvents: method(function (nodeActor, wait) {
// For now, only have one node being polled
this.disableChangeParamEvents();
// Ignore if node is dead
if (!nodeActor.isAlive()) {
return;
}
let previous = mapAudioParams(nodeActor);
// Store the ID of the node being polled
this._pollingID = nodeActor.actorID;
this.poller = new Poller(() => {
// If node has been collected, disable param polling
if (!nodeActor.isAlive()) {
this.disableChangeParamEvents();
return;
}
let current = mapAudioParams(nodeActor);
diffAudioParams(previous, current).forEach(changed => {
this._onChangeParam(nodeActor, changed);
});
previous = current;
}).on(wait || PARAM_POLLING_FREQUENCY);
}, {
request: {
node: Arg(0, "audionode"),
wait: Arg(1, "nullable:number"),
},
oneway: true
}),
disableChangeParamEvents: method(function () {
if (this.poller) {
this.poller.off();
}
this._pollingID = null;
}, {
oneway: true
}),
/**
* Events emitted by this actor.
*/
@@ -502,6 +437,12 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
dest: Option(0, "audionode"),
param: Option(0, "string")
},
"change-param": {
type: "changeParam",
source: Option(0, "audionode"),
param: Option(0, "string"),
value: Option(0, "string")
},
"create-node": {
type: "createNode",
source: Arg(0, "audionode")
@@ -509,13 +450,6 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
"destroy-node": {
type: "destroyNode",
source: Arg(0, "audionode")
},
"change-param": {
type: "changeParam",
param: Option(0, "string"),
newValue: Option(0, "json"),
oldValue: Option(0, "json"),
actorID: Option(0, "string")
}
},
@@ -539,7 +473,7 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
/**
* Takes an XrayWrapper node, and attaches the node's `nativeID`
* to the AudioParams as `_parentID`, as well as the the type of param
* as a string on `_paramName`. Used to tag AudioParams for `connect-param` events.
* as a string on `_paramName`.
*/
_instrumentParams: function (node) {
let type = getConstructorName(node);
@@ -559,14 +493,6 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
* created), so make a new actor and store that.
*/
_getActorByNativeID: function (nativeID) {
// If the WebAudioActor has already been finalized, the `_nativeToActorID`
// map will already be destroyed -- the lingering destruction events
// seem to only occur in e10s, so add an extra check here to disregard
// these late events
if (!this._nativeToActorID) {
return null;
}
// Ensure we have a Number, rather than a string
// return via notification.
nativeID = ~~nativeID;
@@ -618,12 +544,15 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
},
/**
* Called when an AudioParam that's being listened to changes.
* Takes an AudioNodeActor and an object with `newValue`, `oldValue`, and `param` name.
* Called when a parameter changes on an audio node
*/
_onChangeParam: function (actor, changed) {
changed.actorID = actor.actorID;
emit(this, "change-param", changed);
_onParamChange: function (node, param, value) {
let actor = this._getActorByNativeID(node.id);
emit(this, "param-change", {
source: actor,
param: param,
value: value
});
},
/**
@@ -634,8 +563,7 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
emit(this, "create-node", actor);
},
/**
* Called when `webaudio-node-demise` is triggered,
/** Called when `webaudio-node-demise` is triggered,
* and emits the associated actor to the front if found.
*/
_onDestroyNode: function ({data}) {
@@ -648,10 +576,6 @@ let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
// notifications for a document that no longer exists,
// the mapping should not be found, so we do not emit an event.
if (actor) {
// Turn off polling for changes if on for this node
if (this._pollingID === actor.actorID) {
this.disableChangeParamEvents();
}
this._nativeToActorID.delete(nativeID);
emit(this, "destroy-node", actor);
}
@@ -739,109 +663,17 @@ function getConstructorName (obj) {
}
/**
* Create a value grip for `value`, or fallback to a grip-like object
* for renderable information for the front-end for things like Float32Arrays,
* AudioBuffers, without tracking them in an actor pool.
* Create a grip-like object to pass in renderable information
* to the front-end for things like Float32Arrays, AudioBuffers,
* without tracking them in an actor pool.
*/
function createGrip (value) {
try {
return ThreadActor.prototype.createValueGrip(value);
}
catch (e) {
return {
type: "object",
preview: {
kind: "ObjectWithText",
text: ""
},
class: getConstructorName(value)
};
}
function createObjectGrip (value) {
return {
type: "object",
preview: {
kind: "ObjectWithText",
text: ""
},
class: getConstructorName(value)
};
}
/**
* Takes an AudioNodeActor and maps its current parameter values
* to a hash, where the property is the AudioParam name, and value
* is the current value.
*/
function mapAudioParams (node) {
return node.getParams().reduce(function (obj, p) {
obj[p.param] = p.value;
return obj;
}, {});
}
/**
* Takes an object of previous and current values of audio parameters,
* and compares them. If they differ, emit a `change-param` event.
*
* @param Object prev
* Hash of previous set of AudioParam values.
* @param Object current
* Hash of current set of AudioParam values.
*/
function diffAudioParams (prev, current) {
return Object.keys(current).reduce((changed, param) => {
if (!equalGrips(current[param], prev[param])) {
changed.push({
param: param,
oldValue: prev[param],
newValue: current[param]
});
}
return changed;
}, []);
}
/**
* Compares two grip objects to determine if they're equal or not.
*
* @param Any a
* @param Any a
* @return Boolean
*/
function equalGrips (a, b) {
let aType = typeof a;
let bType = typeof b;
if (aType !== bType) {
return false;
} else if (aType === "object") {
// In this case, we are comparing two objects, like an ArrayBuffer or Float32Array,
// or even just plain "null"s (which grip's will have `type` property "null",
// and we have no way of showing more information than its class, so assume
// these are equal since nothing can be updated with information of value.
if (a.type === b.type) {
return true;
}
// Otherwise return false -- this could be a case of a property going from `null`
// to having an ArrayBuffer or an object, in which case we should update it.
return false;
} else {
return a === b;
}
}
/**
* Poller class -- takes a function, and call be turned on and off
* via methods to execute `fn` on the interval specified during `on`.
*/
function Poller (fn) {
this.fn = fn;
}
Poller.prototype.on = function (wait) {
let poller = this;
poller.timer = setTimeout(poll, wait);
function poll () {
poller.fn();
poller.timer = setTimeout(poll, wait);
}
return this;
};
Poller.prototype.off = function () {
if (this.timer) {
clearTimeout(this.timer);
}
return this;
};