Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: slow post message #623

Merged
merged 6 commits into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 24 additions & 19 deletions src/application/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,37 @@ export default class ModV {
});

this.$worker.addEventListener("message", e => {
const message = e.data;
const { type } = message;

// if (
// type !== "metrics/SET_FPS_MEASURE" &&
// type !== "modules/UPDATE_ACTIVE_MODULE_PROP" &&
// type !== "beats/SET_BPM" &&
// type !== "beats/SET_KICK" &&
// type !== "tick"
// ) {
// console.log(`⚙️%c ${type}`, "color: red");
// }

if (e.data.type === "tick" && this.ready) {
this.tick(e.data.payload);
return;
}

const message = e.data;
const { type } = message;

if (Array.isArray(message)) {
return;
}
const payload = e.data.payload ? JSON.parse(e.data.payload) : undefined;

// if (
// type !== "metrics/SET_FPS_MEASURE" &&
// type !== "modules/UPDATE_ACTIVE_MODULE_PROP" &&
// type !== "beats/SET_BPM" &&
// type !== "beats/SET_KICK"
// ) {
if (this.debug) {
console.log(`⚙️%c ${type}`, "color: red", payload);
if (type === "commitQueue") {
for (let i = 0; i < payload.length; i++) {
const commit = payload[i];
store.commit(commit.type, commit.payload);
}
} else {
store.commit(type, payload);
}
// }

store.commit(type, payload);
});

const that = this;
Expand All @@ -92,10 +98,9 @@ export default class ModV {
state: store.state,
getters: store.getters,

async commit(...args) {
return await that.$asyncWorker.postMessage(
commit(...args) {
return that.$worker.postMessage(
{
__async: true,
type: "commit",
identifier: args[0],
payload: args[1]
Expand All @@ -104,8 +109,8 @@ export default class ModV {
);
},

async dispatch(...args) {
return await that.$asyncWorker.postMessage(
dispatch(...args) {
return that.$asyncWorker.postMessage(
{
__async: true,
type: "dispatch",
Expand Down
88 changes: 76 additions & 12 deletions src/application/worker/index.worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,105 @@ async function start() {
const store = require("./store").default;
const loop = require("./loop").default;
const grabCanvasPlugin = require("../plugins/grab-canvas").default;
const get = require("lodash.get");

const { tick: frameTick } = require("./frame-counter");
const { getFeatures, setFeatures } = require("./audio-features");
// const featureAssignmentPlugin = require("../plugins/feature-assignment");

let interval = store.getters["fps/interval"];

const commitQueue = [];

store.subscribe(mutation => {
const { type, payload } = mutation;
const { type: mutationType, payload: mutationPayload } = mutation;

if (type === "beats/SET_BPM" || type === "fps/SET_FPS") {
store.dispatch("tweens/updateBpm", { bpm: payload.bpm });
if (mutationType === "beats/SET_BPM" || mutationType === "fps/SET_FPS") {
store.dispatch("tweens/updateBpm", { bpm: mutationPayload.bpm });
}

if (type === "beats/SET_KICK" && payload.kick === lastKick) {
if (
mutationType === "beats/SET_KICK" &&
mutationPayload.kick === lastKick
) {
return;
} else if (type === "beats/SET_KICK" && payload.kick !== lastKick) {
lastKick = payload.kick;
} else if (
mutationType === "beats/SET_KICK" &&
mutationPayload.kick !== lastKick
) {
lastKick = mutationPayload.kick;
}

if (type === "fps/SET_FPS") {
if (mutationType === "fps/SET_FPS") {
interval = store.getters["fps/interval"];
}

if (
type === "modules/UPDATE_ACTIVE_MODULE" &&
(payload.key !== "props" || payload.key !== "meta")
mutationType === "modules/UPDATE_ACTIVE_MODULE" &&
(mutationPayload.key !== "props" || mutationPayload.key !== "meta")
) {
return;
}

const {
inputs: { inputs, inputLinks }
} = store.state;

// Update mutation type Input Links
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is an input link of type mutation?

Copy link
Member Author

@2xAA 2xAA Aug 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple types of input links.

  • all input links
    • Params:
      • inputId
        • String. The ID of the input to link to
      • ?source
        • String. Identifies what created and/or manages this input link
      • ?min
        • Number. Minimum expected value, will scale the incoming value to the input's min/max if min and max are set
      • ?max
        • Number. Maximum expected value, will scale the incoming value to the input's min/max if min and max are set
  • getter
    • ⚠️ updates every frame
    • Reads a getter in the store, possibly with arguments, defined in ?args, and applies that to the input link defined with inputId
    • Params:
      • location
        • String. location of the getter in the store, e.g. meyda/getFeature
      • ?args
        • Array. arguments for a getter that is a function, e.g. ["energy"]
  • mutation
    • Waits for a mutation type, defined with match.type, possibly checks the mutation payload for specific unique parameters, defined on match.?payload, and applies a store value, specified with location, to the input link defined with inputId
    • Params:
      • location
        • String. location of the value to read in the store, e.g. midi.devices[1].channelData[1][144]
      • match
        • Object. Parameters of the mutation to match. Has keys:
          • type
            • String. Mutation type from the store, e.g. midi/WRITE_DATA
          • ?payload
            • Object. Key-value pairs to match in the mutation payload. e.g.
            {
              id: `${id}-${name}-${manufacturer}`,
              channel,
              type
            } 
  • state
    • ⚠️ updates every frame
    • Reads a location in the store and applies that to the input link defined with inputId
    • Params:
      • location
        • String. location of the value to read in the store, e.g. midi.devices[1].channelData[1][144]

state and getter links are updated in loop.js

if (linkType === "getter" && linkArguments) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@2xAA thanks for this explanation!

const mutationTypeInputLinks = Object.values(inputLinks).filter(
link => link.type === "mutation"
);
const inputLinksLength = mutationTypeInputLinks.length;
for (let i = 0; i < inputLinksLength; ++i) {
const link = mutationTypeInputLinks[i];
const inputId = link.id;
const bind = inputs[inputId];

const { type, location, data } = bind;

const { location: linkLocation, match } = link;

if (match.type !== mutationType) {
continue;
}

let payloadMatches = false;

if (match.payload) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is match?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const matchPayloadKeys = Object.keys(match.payload);
payloadMatches = matchPayloadKeys.every(key => {
const value = match.payload[key];
return value === mutationPayload[key];
});
} else {
payloadMatches = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this true if there is no payload?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's possible for mutations to have no payload, or we just want to update when a mutation, e.g. beats/SET_KICK, is committed.

Example here:

this.$modV.store.dispatch("inputs/createInputLink", {
inputId: this.inputId,
type: "mutation",
location: "beats.kick",
match: {
type: "beats/SET_KICK"
},
source: "meyda",
args: [this.feature]
});

}

if (!payloadMatches) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have no payloadMatch, we continue? Why do we check if the payload is matching anyway? To see if there was a change?

Copy link
Member Author

@2xAA 2xAA Aug 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The payload may need to match to check for things like IDs or other unique parameters so we know the correct inputLink will be updated.

If we didn't check for the right ID on the MIDI input links, all MIDI input links would update on any MIDI input which would be wasteful in terms of postMessage.

continue;
}

const value = get(store.state, linkLocation);

if (type === "action") {
store.dispatch(location, { ...data, data: value });
} else if (type === "commit") {
store.commit(location, { ...data, data: value });
}
}

commitQueue.push(mutation);
});

function sendCommitQueue() {
const commits = JSON.stringify(commitQueue);
commitQueue.splice(0, commitQueue.length);

self.postMessage({
type,
payload: JSON.stringify(payload)
type: "commitQueue",
payload: commits
});
});
}

store.dispatch("plugins/add", grabCanvasPlugin);

Expand Down Expand Up @@ -177,6 +240,7 @@ async function start() {
}

function frameActions(delta) {
sendCommitQueue();
self.postMessage({
type: "tick",
payload: delta
Expand Down
18 changes: 17 additions & 1 deletion src/application/worker/loop.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,24 @@ function loop(delta, features, fftOutput) {
location: linkLocation,
args: linkArguments,
min,
max
max,
source
} = link;

const moduleId = store.state.inputs.inputs[inputId].data.moduleId;

// check if the module is enabled for meyda sources, and if it is not, skip it
// mutation linkTypes are also skipped as they're handled in index.worker.js in
// the store commit subscription
if (
2xAA marked this conversation as resolved.
Show resolved Hide resolved
linkType === "mutation" ||
(source === "meyda" &&
moduleId &&
!store.state.modules.active[moduleId].meta.enabled)
) {
continue;
}

let value;

if (linkType === "getter" && linkArguments) {
Expand Down
88 changes: 86 additions & 2 deletions src/application/worker/store/modules/inputs.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,82 @@
import Vue from "vue";

import uuidv4 from "uuid/v4";
import SWAP from "./common/swap";

/**
* InputLinkType enum string values.
* @enum {String}
*/
// eslint-disable-next-line
const InputLinkType = {
getter: "getter",
mutation: "mutation",
state: "state"
};

/**
* @typedef {Object} InputLink
*
* @property {String} inputId The ID of the @Input to link to
*
* @property {InputLinkType} type The type of the @InputLink
*
* @property {String} source Identifies what created and/or manages this input link
*
* @property {Number} [min] Minimum expected value, will scale the incoming value to
* the input's min/max if min and max are set
*
* @property {Number} [max] Maximum expected value, will scale the incoming value to
* the input's min/max if min and max are set
*/

/**
* @typedef {Object} InputLinkGetterType
*
* @property {String} location location of the getter in the store, e.g. meyda/getFeature
*
* @property {Array} [args] arguments for a getter that is a function, e.g. ["energy"]
*
*
* ⚠️ updates every frame
* Reads a getter in the store, possibly with arguments, defined in ?args, and applies that to the
* input link defined with inputId
* @typedef {InputLink & InputLinkGetterType} InputLinkGetter
*/

/**
* @typedef {Object} InputLinkMutationType
*
* @property {String} location location of the value to read in the store, e.g.
* midi.devices[1].channelData[1][144]
*
* @property {Object} match parameters of the mutation to match
*
* @property {String} match.type Mutation type from the store, e.g. midi/WRITE_DATA
*
* @property {String} [match.payload] Key-value pairs to match in the mutation payload, e.g.
* {
id: `${id}-${name}-${manufacturer}`,
channel: 1,
type: 144,
}
*
* Waits for a mutation type, defined with match.type, possibly checks the mutation payload for
* specific unique parameters, defined on match.?payload, and applies a store value, specified
* with location, to the input link defined with inputId
* @typedef {InputLink & InputLinkMutationType} InputLinkMutation
*/

/**
* @typedef {Object} InputLinkStateType
*
* @property {String} location location of the getter in the store, e.g.
* midi.devices[1].channelData[1][144]
*
* ⚠️ updates every frame
* Reads a location in the store and applies that to the input link defined with inputId
* @typedef {InputLink & InputLinkStateType} InputLinkState
*/

function getDefaultState() {
return {
focusedInput: { id: null, title: null },
Expand Down Expand Up @@ -41,6 +115,7 @@ const actions = {
min = 0,
max = 1,
source,
match,
writeToSwap
}
) {
Expand All @@ -52,7 +127,16 @@ const actions = {
return false;
}

const inputLink = { id: inputId, location, type, args, min, max, source };
const inputLink = {
id: inputId,
location,
type,
args,
min,
max,
source,
match
};
if (!writeTo.inputs[inputId]) {
console.warn(
"Did not create inputLink. Could not find input with id",
Expand Down
5 changes: 4 additions & 1 deletion src/components/InputLinkComponents/AudioFeatures.vue
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,11 @@ export default {
if (this.feature === "kick") {
this.$modV.store.dispatch("inputs/createInputLink", {
inputId: this.inputId,
type: "state",
type: "mutation",
location: "beats.kick",
match: {
type: "beats/SET_KICK"
},
source: "meyda",
args: [this.feature]
});
Expand Down
10 changes: 9 additions & 1 deletion src/components/InputLinkComponents/MIDI.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,17 @@ export default {

this.hasLink = await this.$modV.store.dispatch("inputs/createInputLink", {
inputId: this.inputId,
type: "state",
type: "mutation",
source: "midi",
location: `midi.devices['${id}-${name}-${manufacturer}'].channelData['${channel}']['${type}']`,
match: {
type: "midi/WRITE_DATA",
payload: {
id: `${id}-${name}-${manufacturer}`,
channel,
type
}
},
min: 0,
max: 1
});
Expand Down