recording and setKeyframes entanglement

This commit is contained in:
jrkb 2023-10-07 12:21:36 +02:00
parent 7f6ab572c7
commit de5550287c
6 changed files with 738 additions and 552 deletions

File diff suppressed because it is too large Load diff

View file

@ -91,6 +91,7 @@ const config = {
}, },
record: { record: {
ignoreProps: ['fontVariationAxes','letterDelays','color'], ignoreProps: ['fontVariationAxes','letterDelays','color'],
recordMapped: true,
}, },
midi: { midi: {
touchTimeThreshold_s: 0.5, touchTimeThreshold_s: 0.5,

View file

@ -188,7 +188,6 @@ window.onload = () => {
alert('Sorry, Variable Time is a tool currently designed to be used on desktop!'); alert('Sorry, Variable Time is a tool currently designed to be used on desktop!');
} }
window.addEventListener('panelEvent', (e) => { window.addEventListener('panelEvent', (e) => {
console.log('debug panelEvent received', e);
clearTimeout(window.panelFinderTimeout); clearTimeout(window.panelFinderTimeout);
let target = false; let target = false;
if (e.detail.panelID === 'artboard') { if (e.detail.panelID === 'artboard') {
@ -204,7 +203,6 @@ window.onload = () => {
} }
}); });
window.addEventListener('sequenceEvent', (e) => { window.addEventListener('sequenceEvent', (e) => {
console.log('debug sequenceEvent received', e);
let target = false; let target = false;
if (e.detail.panelID === 'artboard') { if (e.detail.panelID === 'artboard') {
target = artboard; target = artboard;

View file

@ -137,7 +137,7 @@ const Record = function(tp) {
return hot.hasOwnProperty(layerID) return hot.hasOwnProperty(layerID)
&& hot[layerID].hasOwnProperty(propTitle); && hot[layerID].hasOwnProperty(propTitle);
}; };
const makeHot = (layerID, propTitle) => { const addHot = (layerID, propTitle) => {
if (!isHot(layerID, propTitle)) { if (!isHot(layerID, propTitle)) {
if (!hot.hasOwnProperty(layerID)) { if (!hot.hasOwnProperty(layerID)) {
hot[layerID] = {}; hot[layerID] = {};
@ -146,11 +146,36 @@ const Record = function(tp) {
recording: [], recording: [],
}; };
} }
const button = tp buffy.register(layerID);
.getPanelPropContainer(propTitle) // handle UI only if layer is selected
.querySelector('.recordButton'); if (getLayer(layerID).isSelected()) {
if (button !== null) { const button = tp
button.classList.add('active'); .getPanelPropContainer(propTitle)
.querySelector('.recordButton');
if (button !== null) {
button.classList.add('active');
}
}
};
const removeHot = (layerID, propTitle) => {
if (isHot(layerID, propTitle)) {
delete hot[layerID][propTitle];
}
// what if it is the last prop in the layer
if (hot.hasOwnProperty(layerID)) {
if (Object.keys(hot[layerID]).length === 0) {
delete hot[layerID];
buffy.deregister(layerID);
}
}
// handle UI only if layer is selected
if (getLayer(layerID).isSelected()) {
const button = tp
.getPanelPropContainer(propTitle)
.querySelector('.recordButton');
if (button !== null) {
button.classList.remove('active');
}
} }
}; };
//const makeNotHot = (layerID, propTitle) => { //const makeNotHot = (layerID, propTitle) => {
@ -181,21 +206,27 @@ const Record = function(tp) {
if(isRecording) { if(isRecording) {
stopRecording(); stopRecording();
} else { } else {
Object.keys(audio.mapping) if (config.record.recordMapped) {
.forEach((layerID) => { // make all mapped props hot and
if (getLayer(layerID).isSelected()) { Object.keys(audio.mapping)
Object.keys(audio.mapping[layerID]) .forEach((layerID) => {
.forEach((propTitle) => { if (getLayer(layerID).isSelected()) {
makeHot(layerID, propTitle); Object.keys(audio.mapping[layerID])
.forEach((propTitle) => {
addHot(layerID, propTitle);
});
}
}); });
buffy.register(layerID); } else {
} // only make this propTitle hot and
}); // register its layer for recording
addHot(layer.id(), propTitle);
}
startRecording(); startRecording();
} }
}); });
console.log("Record::addRecordButton", //console.log("Record::addRecordButton",
`added a record button for ${propTitle}`); //`added a record button for ${propTitle}`);
} }
} else { } else {
console.log("Record::addRecordButton", console.log("Record::addRecordButton",
@ -300,7 +331,7 @@ const Record = function(tp) {
lastPosition = position; lastPosition = position;
}); });
} else { } else {
console.log('whoops input_clone is null'); console.log('Record::startRecording', `whoops input_clone for ${propTitle} is null`);
} }
}); });
tp.sheet.sequence.position = 0; tp.sheet.sequence.position = 0;
@ -310,11 +341,7 @@ const Record = function(tp) {
}; };
const stopRecording = () => { const stopRecording = () => {
return new Promise((resolve) => { return new Promise((resolve) => {
console.log('stoprecording');
const layerKeys = Object.keys(hot); const layerKeys = Object.keys(hot);
console.log('stopRecording', 'layerKeys', {
layerKeys
}, 'hot', JSON.stringify(hot));
const promises = []; const promises = [];
promises.push(() => { promises.push(() => {
return new Promise((subResolve) => { return new Promise((subResolve) => {
@ -329,23 +356,19 @@ const Record = function(tp) {
}); });
}); });
layerKeys.forEach((layerID) => { layerKeys.forEach((layerID) => {
console.log('stopRecording', layerID);
const layer = getLayer(layerID); const layer = getLayer(layerID);
const propTitles = Object.keys(hot[layerID]); const propTitles = Object.keys(hot[layerID]);
const keyframes = []; const keyframes = [];
propTitles.forEach((propTitle) => { propTitles.forEach((propTitle) => {
console.log('stopRecording', propTitle);
// NOTE: layerID is not actually used atm // NOTE: layerID is not actually used atm
// and should be the layer anyways // and should be the layer anyways
uncloneInput(layerID, propTitle); uncloneInput(layerID, propTitle);
console.log('stopRecording', 'should have uncloned input for ' + propTitle);
keyframes.push({ keyframes.push({
path: [propTitle], path: [propTitle],
keyframes: hot[layerID][propTitle].recording, keyframes: hot[layerID][propTitle].recording,
}); });
}); });
//setTimeout(() => { //setTimeout(() => {
console.log('stopRecording', 'adding the keyframes now because we wnat it to happen right now please', keyframes);
promises.push(() => { promises.push(() => {
return new Promise((subResolve) => { return new Promise((subResolve) => {
tp.setKeyframes(layer, keyframes).then(() => { tp.setKeyframes(layer, keyframes).then(() => {
@ -358,17 +381,12 @@ const Record = function(tp) {
}); });
sequencialPromises(promises, () => { sequencialPromises(promises, () => {
Object.keys(hot).forEach((layerID) => { Object.keys(hot).forEach((layerID) => {
buffy.deregister(layerID);
Object.keys(hot[layerID]).forEach((propTitle) => { Object.keys(hot[layerID]).forEach((propTitle) => {
delete hot[layerID][propTitle]; removeHot(layerID, propTitle);
if (Object.keys(hot[layerID]).length === 0) {
delete hot[layerID];
}
const button = tp.getPanel().querySelector(`.recordButton${propTitle}`);
button.classList.remove('active');
}); });
buffy.deregister(layerID);
}); });
console.log('stopRecording', 'absolutely stopped recording'); console.log('Record::stopRecording', 'stopped recording');
isRecording = false; isRecording = false;
resolve(); resolve();
}); });
@ -377,6 +395,8 @@ const Record = function(tp) {
// public // public
this.addRecordButton = addRecordButton; this.addRecordButton = addRecordButton;
this.addHot = addHot;
this.removeHot = removeHot;
this.getHot = () => { this.getHot = () => {
return hot; return hot;
}; };

View file

@ -135,48 +135,84 @@ const TheatrePlay = function(autoInit = false) {
return keyframes; return keyframes;
}; };
const getSequenceButton = (path) => { const getSequenceButton = (path) => {
let t = getPanelPropTitle(path.join('.')); let t = getPanelPropTitle(Array.isArray(path) ? path.join('.') : path);
if (t === null) { if (t === null) {
return null; return null;
} }
return t.parentElement.querySelector('[title="Sequence this prop"]'); return t.parentElement.querySelector('[title="Sequence this prop"]');
}; };
const isSequenced = (path) => {
return getSequenceButton(path) === null;
};
const setSequenced = (propTitle, sequenced) => { const setSequenced = (propTitle, sequenced, metaResolve = false) => {
return new Promise((resolve) => { const f = (resolve) => {
const contextItem = sequenced ? 'sequence' : 'make static'; const propIsSequenced = isSequenced(propTitle);
const antiContextItem = sequenced ? 'make static' : 'sequence'; const somethingToDo = sequenced !== propIsSequenced;
const finishedSequencedEvent = (e) => { if (somethingToDo) {
tp.getPanel().removeEventListener('injected', finishedSequencedEvent); const contextItem = sequenced ? 'sequence' : 'make static';
console.log('debug FINISHED SEQUENCED EVENT', e, propTitle); const antiContextItem = sequenced ? 'make static' : 'sequence';
resolve(true);
};
const clickContextMenu = () => { const finishedSequencedEvent = (e) => {
let done = false; // only care about events from our prop
tp.getPanelPropTitle(propTitle).removeEventListener('contextmenu', clickContextMenu); if (propTitle === e.detail.prop.join('.')) {
tp.shadowRoot.querySelectorAll('ul li span').forEach((s) => { // if we un-sequence, we listen to stateEditors' event
if (s.innerHTML.toLowerCase() === contextItem.toLowerCase()) { if (!sequenced && e.detail.origin === 'stateEditors.ts' && e.detail.sequenced === sequenced) {
tp.getPanel().addEventListener('injected', finishedSequencedEvent); window.removeEventListener('sequenceEvent', finishedSequencedEvent);
s.click(); resolve(true);
console.log('debug click');
done = true; // if we sequence, then we wait until the track is there
} else if (s.innerHTML.toLowerCase() === antiContextItem.toLowerCase()) { } else if (sequenced && e.detail.origin === 'BasicKeyframedTrack.tsx' && e.detail.sequenced === sequenced) {
done = true; window.removeEventListener('sequenceEvent', finishedSequencedEvent);
resolve(false); resolve(true);
} else {
console.log('TheatrePlayu::setSequenced', 'ignored event', e, e.detail);
}
} }
}); };
if (!done) {
setTimeout(() => {
clickContextMenu();
}, 100);
}
};
getPanelPropTitle(propTitle).addEventListener('contextmenu', clickContextMenu); let counter = 0;
getPanelPropTitle(propTitle).dispatchEvent(new Event('contextmenu')); const clickContextMenu = (e) => {
}); let done = false;
if (e.target !== null) {
e.target.removeEventListener('contextmenu', clickContextMenu);
}
tp.shadowRoot.querySelectorAll('ul li span').forEach((s) => {
if (s.innerHTML.toLowerCase() === contextItem.toLowerCase()) {
window.addEventListener('sequenceEvent', finishedSequencedEvent);
s.click();
done = true;
} else if (s.innerHTML.toLowerCase() === antiContextItem.toLowerCase()) {
done = true;
resolve(false);
}
});
if (!done) {
setTimeout(() => {
if (counter < 4) {
clickContextMenu(e);
counter++;
} else {
setSequenced(propTitle, sequenced, resolve);
}
}, 100);
}
};
getPanelPropTitle(propTitle).addEventListener('contextmenu', clickContextMenu);
getPanelPropTitle(propTitle).dispatchEvent(new Event('contextmenu'));
} else {
resolve();
}
};
if (!metaResolve) {
return new Promise((resolve) => {
f(resolve);
});
} else {
f(metaResolve);
}
}; };
const addKeyframes = (layer, keyframes) => { const addKeyframes = (layer, keyframes) => {
@ -187,48 +223,53 @@ const TheatrePlay = function(autoInit = false) {
} }
const existingKeyframes = getKeyframes(layer); const existingKeyframes = getKeyframes(layer);
const promises = []; const promises = [];
const ms = 0;//config.tp.addKeyframesTimeout_s * 1000; const ms = 0; //config.tp.addKeyframesTimeout_s * 1000;
keyframes.forEach((k) => { keyframes.forEach((k) => {
let prop = layer.theatreObject.props; let prop = layer.theatreObject.props;
for (let i = 0; i < k.path.length; i++) { for (let i = 0; i < k.path.length; i++) {
prop = prop[k.path[i]]; prop = prop[k.path[i]];
} }
const position = tp.sheet.sequence.position; const position = tp.sheet.sequence.position;
// NOTE: can we sequence values without pretend clicking? promises.push(() => {
const sequenceButton = getSequenceButton(k.path); return new Promise((subResolve) => {
if (sequenceButton !== null) {
promises.push(() => { return new Promise((subResolve) => {
setTimeout(() => { setTimeout(() => {
sequenceButton.click(); if (layer.isSelected()) {
const detectSE = (e) => { setSequenced(k.path.join('.'), true)
if (e.detail.panelID === layer.id()) { .then(() => {
window.removeEventListener('sequenceEvent',detectSE);
console.log('received sequenceEvent',e);
const f = (e) => {
tp.getPanel().removeEventListener('injected', f);
subResolve(); subResolve();
}; });
tp.getPanel().addEventListener('injected', f); } else {
} // we cannot select layers without pseudoclicking
}; // so let's wait for a happy 'injected' event that
window.addEventListener('sequenceEvent', detectSE); // closes off the selection
}, ms);// * promises.length); //
})}); // first, the listener callback
} else { const f = () => {
//console.error(k.path, 'did not find sequence button'); tp.getPanel().removeEventListener('injected', f);
// is (probably) already sequenced setSequenced(k.path.join('.'), true)
} .then(() => {
subResolve();
});
};
// then add it
tp.getPanel().addEventListener('injected', f);
// and fire the click
layer.select();
}
}, ms); // * promises.length);
})
});
let propHasKeyframesAt = -1; let propHasKeyframesAt = -1;
if (existingKeyframes !== null && if (existingKeyframes !== null &&
existingKeyframes !== false && existingKeyframes !== false &&
typeof existingKeyframes !== 'undefined' && typeof existingKeyframes !== 'undefined' &&
Array.isArray(existingKeyframes)) { Array.isArray(existingKeyframes)) {
existingKeyframes.forEach((existingK, existingKI) => { existingKeyframes.forEach((existingK, existingKI) => {
if (arraysEqual(k.path, existingK.path)) { if (arraysEqual(k.path, existingK.path)) {
propHasKeyframesAt = existingKI; propHasKeyframesAt = existingKI;
} }
}); });
} }
k.keyframes.forEach((keyframe) => { k.keyframes.forEach((keyframe) => {
let alreadyThere = false; let alreadyThere = false;
if (propHasKeyframesAt >= 0) { if (propHasKeyframesAt >= 0) {
@ -240,29 +281,33 @@ const TheatrePlay = function(autoInit = false) {
}); });
} }
if (!alreadyThere) { if (!alreadyThere) {
promises.push(() => { return new Promise((subResolve) => { promises.push(() => {
setTimeout(() => { return new Promise((subResolve) => {
tp.sheet.sequence.position = keyframe.position; setTimeout(() => {
this.studio.transaction(({ tp.sheet.sequence.position = keyframe.position;
set this.studio.transaction(({
}) => { set
set(prop, keyframe.value); }) => {
subResolve(); set(prop, keyframe.value);
}); subResolve();
}, ms);// * promises.length); });
})}); }, ms); // * promises.length);
})
});
} }
}); });
promises.push(() => { return new Promise((subResolve) => { promises.push(() => {
setTimeout(() => { return new Promise((subResolve) => {
tp.sheet.sequence.position = position; setTimeout(() => {
subResolve(); tp.sheet.sequence.position = position;
}, ms);// * promises.length); subResolve();
})}); }, ms); // * promises.length);
})
});
}); });
sequencialPromises(promises, resolve); sequencialPromises(promises, resolve);
//Promise.all(promises).then(() => { //Promise.all(promises).then(() => {
//resolve(); //resolve();
//}); //});
}); });
}; };
@ -273,19 +318,28 @@ const TheatrePlay = function(autoInit = false) {
return false; return false;
} }
const promises = []; const promises = [];
let waitify = false;
keyframes.forEach((k) => { keyframes.forEach((k) => {
promises.push(new Promise((subResolve) => { const propTitle = k.path.join('.');
const propTitle = k.path.join('.'); if (isSequenced(propTitle)) {
setSequenced(propTitle, false) waitify = true;
.then(subResolve); promises.push(() => {
})); return new Promise((subResolve) => {
setSequenced(propTitle, false)
.then(() => {
subResolve();
});
});
});
}
}); });
Promise sequencialPromises(promises, () => {
.all(promises) const timeout_ms = waitify ? 1000 : 0;
.then(() => { setTimeout(() => {
addKeyframes(layer, keyframes) addKeyframes(layer, keyframes)
.then(resolve); .then(resolve);
}); }, timeout_ms);
});
}); });
}; };
@ -358,6 +412,8 @@ const TheatrePlay = function(autoInit = false) {
// remove object from objects list // remove object from objects list
delete theatreObjects[name]; delete theatreObjects[name];
}; };
this.isSequenced = isSequenced;
this.getSequenceButton = getSequenceButton;
this.getSequencePanelLeft = getSequencePanelLeft; this.getSequencePanelLeft = getSequencePanelLeft;
this.getPanel = getPanel; this.getPanel = getPanel;
this.getPanelPropTitle = getPanelPropTitle; this.getPanelPropTitle = getPanelPropTitle;

View file

@ -395,11 +395,21 @@ const isMobile = () => {
return false; return false;
}; };
// NOTE:
// promises must be delivered inside a function like:
//
// const promises = [];
//
// promises.push(() => { return new Promise((resolve) => { console.log('lalala ONE'); resolve() }); });
// promises.push(() => { return new Promise((resolve) => { console.log('lalala TWO'); resolve() }); });
// promises.push(() => { return new Promise((resolve) => { console.log('lalala THREE'); resolve() }); });
//
// sequencialPromises(promises, () => { console.log('i am done'); });
const sequencialPromises = async (iterable, callback = false) => { const sequencialPromises = async (iterable, callback = false) => {
for (const x of iterable) { for (const x of iterable) {
await x(); await x();
} }
if (callback !== false) { if (typeof callback === 'function') {
callback(); callback();
} }
}; };