recording and setKeyframes entanglement
This commit is contained in:
parent
7f6ab572c7
commit
de5550287c
6 changed files with 738 additions and 552 deletions
|
@ -5,10 +5,36 @@ import {
|
||||||
|
|
||||||
window.mapValue = mapValue;
|
window.mapValue = mapValue;
|
||||||
|
|
||||||
|
|
||||||
|
const AudioMappingOptions = function() {
|
||||||
|
this.freq_min = 0.0;
|
||||||
|
this.freq_max = config.audio.fftBandsUsed;
|
||||||
|
this.min_out = 0.0;
|
||||||
|
this.max_out = 1.0;
|
||||||
|
this.smoothing = config.audio.defaultSmoothing;
|
||||||
|
this.sync = 'volume';
|
||||||
|
this.value = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
const Audio = function(tp, record) {
|
const Audio = function(tp, record) {
|
||||||
|
|
||||||
const audioDom = document.querySelector('.audioWrapper');
|
const audioDom = document.querySelector('.audioWrapper');
|
||||||
|
let audioCtx = false;
|
||||||
const heading = audioDom.querySelector("h1");
|
const heading = audioDom.querySelector("h1");
|
||||||
heading.textContent = "CLICK HERE TO START";
|
heading.textContent = "CLICK HERE TO START";
|
||||||
|
|
||||||
|
// an array of possible sync options.
|
||||||
|
const audio_sync_options = ['volume', 'pitch', 'frequency'];
|
||||||
|
// could also be an enum
|
||||||
|
// like that
|
||||||
|
//const AudioSyncOptions = Object.freeze({
|
||||||
|
//RED: Symbol("volume"),
|
||||||
|
//BLUE: Symbol("pitch"),
|
||||||
|
//GREEN: Symbol("frequency"),
|
||||||
|
//toString: (e) => {
|
||||||
|
//return e.toString.match(/\(([\S\s]*)\)/)[1]
|
||||||
|
//},
|
||||||
|
//});
|
||||||
//document.body.addEventListener("click", init);
|
//document.body.addEventListener("click", init);
|
||||||
let started = false;
|
let started = false;
|
||||||
|
|
||||||
|
@ -16,14 +42,66 @@ const Audio = function(tp, record) {
|
||||||
const canvass = [];
|
const canvass = [];
|
||||||
const canvasCtxs = [];
|
const canvasCtxs = [];
|
||||||
|
|
||||||
|
const isMapped = (layer, propTitle) => {
|
||||||
|
if (!mapping.hasOwnProperty(layer.id())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!mapping[layer.id()].hasOwnProperty(propTitle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addAudioMapping = (layer, propTitle, options = new AudioMappingOptions()) => {
|
||||||
|
if (!mapping.hasOwnProperty(layer.id())) {
|
||||||
|
mapping[layer.id()] = {};
|
||||||
|
}
|
||||||
|
if (!mapping[layer.id()].hasOwnProperty(propTitle)) {
|
||||||
|
mapping[layer.id()][propTitle] = options;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// already there
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeAudioMapping = (layer = false, propTitle = false) => {
|
||||||
|
if (!layer && !propTitle) {
|
||||||
|
Object.keys(mapping).forEach((layerID) => {
|
||||||
|
Object.keys(mapping[layerID]).forEach((propTitle) => {
|
||||||
|
delete mapping[layerID][propTitle];
|
||||||
|
});
|
||||||
|
delete mapping[layerID];
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!mapping.hasOwnProperty(layer.id())) {
|
||||||
|
// no layer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!mapping[layer.id()].hasOwnProperty(propTitle)) {
|
||||||
|
// no propTitle
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delete mapping[layer.id()][propTitle];
|
||||||
|
if (Object.keys(mapping[layer.id()]).length === 0) {
|
||||||
|
delete mapping[layer.id()];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const addAudioOptions = (layer, propTitle) => {
|
const addAudioOptions = (layer, propTitle) => {
|
||||||
|
if (!started) {
|
||||||
|
// audioOptions need a started init
|
||||||
|
init();
|
||||||
|
}
|
||||||
const panelPropTitle = tp.getPanelPropTitle(propTitle);
|
const panelPropTitle = tp.getPanelPropTitle(propTitle);
|
||||||
if (panelPropTitle === null) {
|
if (panelPropTitle === null) {
|
||||||
console.log('Audio::addAudioOptions::error',`cannot find panelPropTitle "${propTitle}"`);
|
console.log('Audio::addAudioOptions::error',`cannot find panelPropTitle "${propTitle}"`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tp.getPanel().querySelector(`.audioOptions${propTitle}`) !== null) {
|
if (tp.getPanel().querySelector(`.audioOptions${propTitle}`) !== null) {
|
||||||
console.log('Audio::addAudioOptions::error',`audioOptions already exist for "${propTitle}"`);
|
//console.log('Audio::addAudioOptions::error',`audioOptions already exist for "${propTitle}"`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const container = tp.getPanelPropContainer(panelPropTitle);
|
const container = tp.getPanelPropContainer(panelPropTitle);
|
||||||
|
@ -38,9 +116,6 @@ const Audio = function(tp, record) {
|
||||||
audioOptions.style.background = 'rgba(0,255,255,0.2)';
|
audioOptions.style.background = 'rgba(0,255,255,0.2)';
|
||||||
audioOptions.style.order = parseInt(container.style.order) + 1;
|
audioOptions.style.order = parseInt(container.style.order) + 1;
|
||||||
|
|
||||||
mappingOptions.freq_min = 0;
|
|
||||||
mappingOptions.freq_max = config.audio.fftBandsUsed;
|
|
||||||
|
|
||||||
const updateMappingOptions = () => {
|
const updateMappingOptions = () => {
|
||||||
mappingOptions.min_out = parseFloat(panel.querySelector(`#audio_min${propTitle}`).value);
|
mappingOptions.min_out = parseFloat(panel.querySelector(`#audio_min${propTitle}`).value);
|
||||||
mappingOptions.max_out = parseFloat(panel.querySelector(`#audio_max${propTitle}`).value);
|
mappingOptions.max_out = parseFloat(panel.querySelector(`#audio_max${propTitle}`).value);
|
||||||
|
@ -59,7 +134,7 @@ const Audio = function(tp, record) {
|
||||||
min_inputDom.type = 'number';
|
min_inputDom.type = 'number';
|
||||||
min_inputDom.name = `audio_min${propTitle}`;
|
min_inputDom.name = `audio_min${propTitle}`;
|
||||||
min_inputDom.id = `audio_min${propTitle}`;
|
min_inputDom.id = `audio_min${propTitle}`;
|
||||||
min_inputDom.value = '0';
|
min_inputDom.value = `${mappingOptions.min_out}`;
|
||||||
const max_inputDom_label = document.createElement('label');
|
const max_inputDom_label = document.createElement('label');
|
||||||
max_inputDom_label.for = 'audio_max';
|
max_inputDom_label.for = 'audio_max';
|
||||||
max_inputDom_label.innerHTML = 'audio_max';
|
max_inputDom_label.innerHTML = 'audio_max';
|
||||||
|
@ -67,7 +142,7 @@ const Audio = function(tp, record) {
|
||||||
max_inputDom.type = 'number';
|
max_inputDom.type = 'number';
|
||||||
max_inputDom.name = `audio_max${propTitle}`;
|
max_inputDom.name = `audio_max${propTitle}`;
|
||||||
max_inputDom.id = `audio_max${propTitle}`;
|
max_inputDom.id = `audio_max${propTitle}`;
|
||||||
max_inputDom.value = '255';
|
max_inputDom.value = `${mappingOptions.max_out}`;
|
||||||
const smoothing_inputDom_label = document.createElement('label');
|
const smoothing_inputDom_label = document.createElement('label');
|
||||||
smoothing_inputDom_label.for = 'audio_smoothing';
|
smoothing_inputDom_label.for = 'audio_smoothing';
|
||||||
smoothing_inputDom_label.innerHTML = 'audio_smoothing';
|
smoothing_inputDom_label.innerHTML = 'audio_smoothing';
|
||||||
|
@ -75,7 +150,7 @@ const Audio = function(tp, record) {
|
||||||
smoothing_inputDom.type = 'number';
|
smoothing_inputDom.type = 'number';
|
||||||
smoothing_inputDom.name = `audio_smoothing${propTitle}`;
|
smoothing_inputDom.name = `audio_smoothing${propTitle}`;
|
||||||
smoothing_inputDom.id = `audio_smoothing${propTitle}`;
|
smoothing_inputDom.id = `audio_smoothing${propTitle}`;
|
||||||
smoothing_inputDom.value = config.audio.defaultSmoothing;
|
smoothing_inputDom.value = mappingOptions.smoothing;
|
||||||
smoothing_inputDom.min = 0;
|
smoothing_inputDom.min = 0;
|
||||||
smoothing_inputDom.max = 1;
|
smoothing_inputDom.max = 1;
|
||||||
smoothing_inputDom.step = 0.01;
|
smoothing_inputDom.step = 0.01;
|
||||||
|
@ -92,8 +167,7 @@ const Audio = function(tp, record) {
|
||||||
sync_titleDom.innerHTML = 'sync with:';
|
sync_titleDom.innerHTML = 'sync with:';
|
||||||
sync_Dom.append(sync_titleDom);
|
sync_Dom.append(sync_titleDom);
|
||||||
|
|
||||||
const sync_options = ['volume', 'pitch', 'frequency'];
|
audio_sync_options.forEach((o, oi) => {
|
||||||
sync_options.forEach((o, oi) => {
|
|
||||||
const sync_inputDom_label = document.createElement('label');
|
const sync_inputDom_label = document.createElement('label');
|
||||||
sync_inputDom_label.for = `audio_sync${o}`;
|
sync_inputDom_label.for = `audio_sync${o}`;
|
||||||
sync_inputDom_label.innerHTML = o;
|
sync_inputDom_label.innerHTML = o;
|
||||||
|
@ -103,7 +177,7 @@ const Audio = function(tp, record) {
|
||||||
sync_inputDom.id = `audio_sync${propTitle}${o}`;
|
sync_inputDom.id = `audio_sync${propTitle}${o}`;
|
||||||
sync_inputDom.value = o;
|
sync_inputDom.value = o;
|
||||||
// default select first option
|
// default select first option
|
||||||
if (oi === 0) {
|
if (o === mappingOptions.sync) {
|
||||||
sync_inputDom.checked = '1';
|
sync_inputDom.checked = '1';
|
||||||
}
|
}
|
||||||
sync_Dom.append(sync_inputDom_label);
|
sync_Dom.append(sync_inputDom_label);
|
||||||
|
@ -158,27 +232,40 @@ const Audio = function(tp, record) {
|
||||||
//removeAudioOptions();
|
//removeAudioOptions();
|
||||||
container.after(audioOptions);
|
container.after(audioOptions);
|
||||||
|
|
||||||
|
const audioButton = container.querySelector('.audioButton');
|
||||||
|
audioButton.classList.add('active');
|
||||||
|
|
||||||
canvass.push(fft_imgDom);
|
canvass.push(fft_imgDom);
|
||||||
canvasCtxs.push(fft_imgDom.getContext("2d"));
|
canvasCtxs.push(fft_imgDom.getContext("2d"));
|
||||||
|
|
||||||
updateMappingOptions();
|
updateMappingOptions();
|
||||||
mappingOptions.value = mappingOptions.min_out;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeAudioOptions = (propTitle = '') => {
|
const removeAudioOptions = (layer = false, propTitle = false) => {
|
||||||
const panel = tp.getPanel();
|
const panel = tp.getPanel();
|
||||||
if (propTitle === '') {
|
if (!layer && !propTitle) {
|
||||||
const otherAudioOptions = panel.querySelectorAll('.audioOptions');
|
const allAudioOptions = panel.querySelectorAll('.audioOptions');
|
||||||
if (otherAudioOptions !== null) {
|
if (allAudioOptions !== null) {
|
||||||
for (let i = 0; i < otherAudioOptions.length; i++) {
|
for (let i = 0; i < allAudioOptions.length; i++) {
|
||||||
otherAudioOptions[i].remove();
|
allAudioOptions[i].remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
panel.querySelectorAll('.audioButton').forEach((button) => {
|
||||||
|
button.classList.remove('active');
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// only selected layers have options
|
||||||
|
// otherwise the ui is not there
|
||||||
|
if (layer.isSelected()) {
|
||||||
const audioOptions = panel.querySelector(`.audioOptions${propTitle}`);
|
const audioOptions = panel.querySelector(`.audioOptions${propTitle}`);
|
||||||
if (audioOptions !== null) {
|
if (audioOptions !== null) {
|
||||||
audioOptions.remove();
|
audioOptions.remove();
|
||||||
}
|
}
|
||||||
|
const audioButton = panel.querySelector(`.audioButton${propTitle}`);
|
||||||
|
if (audioButton !== null) {
|
||||||
|
audioButton.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -205,24 +292,16 @@ const Audio = function(tp, record) {
|
||||||
if (!started) {
|
if (!started) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
if (!mapping.hasOwnProperty(layer.id())) {
|
if (!isMapped(layer, propTitle)) {
|
||||||
mapping[layer.id()] = {};
|
addAudioMapping(layer, propTitle);
|
||||||
}
|
|
||||||
if (!mapping[layer.id()].hasOwnProperty(propTitle)) {
|
|
||||||
mapping[layer.id()][propTitle] = {};
|
|
||||||
button.classList.add('active');
|
|
||||||
addAudioOptions(layer, propTitle);
|
addAudioOptions(layer, propTitle);
|
||||||
} else {
|
} else {
|
||||||
delete mapping[layer.id()][propTitle];
|
removeAudioMapping(layer, propTitle);
|
||||||
if (Object.keys(mapping[layer.id()]).length === 0) {
|
removeAudioOptions(layer, propTitle);
|
||||||
delete mapping[layer.id()];
|
|
||||||
}
|
|
||||||
button.classList.remove('active');
|
|
||||||
removeAudioOptions(propTitle);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
button.classList.add('active');
|
addAudioMapping(layer, propTitle);
|
||||||
addAudioOptions(layer, propTitle);
|
addAudioOptions(layer, propTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,8 +326,13 @@ const Audio = function(tp, record) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function init() {
|
const init = () => {
|
||||||
|
if (!started) {
|
||||||
started = true;
|
started = true;
|
||||||
|
if (audioCtx !== false && audioCtx.state === 'suspended') {
|
||||||
|
audioCtx.resume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
heading.textContent = "Voice-change-O-matic";
|
heading.textContent = "Voice-change-O-matic";
|
||||||
//document.body.removeEventListener("click", init);
|
//document.body.removeEventListener("click", init);
|
||||||
|
|
||||||
|
@ -285,7 +369,7 @@ const Audio = function(tp, record) {
|
||||||
|
|
||||||
// Set up forked web audio context, for multiple browsers
|
// Set up forked web audio context, for multiple browsers
|
||||||
// window. is needed otherwise Safari explodes
|
// window. is needed otherwise Safari explodes
|
||||||
const audioCtx = new(window.AudioContext || window.webkitAudioContext)();
|
audioCtx = new(window.AudioContext || window.webkitAudioContext)();
|
||||||
const voiceSelect = audioDom.querySelector("#voice");
|
const voiceSelect = audioDom.querySelector("#voice");
|
||||||
let source;
|
let source;
|
||||||
let stream;
|
let stream;
|
||||||
|
@ -492,7 +576,7 @@ const Audio = function(tp, record) {
|
||||||
if (mapping.hasOwnProperty(layer.id())) {
|
if (mapping.hasOwnProperty(layer.id())) {
|
||||||
Object.keys(mapping[layer.id()]).forEach((propTitle) => {
|
Object.keys(mapping[layer.id()]).forEach((propTitle) => {
|
||||||
const m = mapping[layer.id()][propTitle];
|
const m = mapping[layer.id()][propTitle];
|
||||||
switch(m.sync) {
|
switch (m.sync) {
|
||||||
case 'volume': {
|
case 'volume': {
|
||||||
let a = mapValue(max_v, 0, 255, m.min_out, m.max_out, true);
|
let a = mapValue(max_v, 0, 255, m.min_out, m.max_out, true);
|
||||||
m.value = m.value * m.smoothing + (1.0 - m.smoothing) * a;
|
m.value = m.value * m.smoothing + (1.0 - m.smoothing) * a;
|
||||||
|
@ -506,7 +590,7 @@ const Audio = function(tp, record) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'pitch': {
|
case 'pitch': {
|
||||||
let a = mapValue(max_i, 0, bufferLengthAlt-1, m.min_out, m.max_out, true);
|
let a = mapValue(max_i, 0, bufferLengthAlt - 1, m.min_out, m.max_out, true);
|
||||||
m.value = m.value * m.smoothing + (1.0 - m.smoothing) * a;
|
m.value = m.value * m.smoothing + (1.0 - m.smoothing) * a;
|
||||||
propsToSet.push({
|
propsToSet.push({
|
||||||
layer,
|
layer,
|
||||||
|
@ -520,8 +604,7 @@ const Audio = function(tp, record) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (m.sync === 'volume') {
|
if (m.sync === 'volume') {}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -683,10 +766,28 @@ const Audio = function(tp, record) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
const deinit = () => {
|
||||||
|
if (started) {
|
||||||
|
if (audioCtx !== false) {
|
||||||
|
audioCtx.suspend();
|
||||||
|
}
|
||||||
|
started = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getContext = () => {
|
||||||
|
return audioCtx;
|
||||||
|
};
|
||||||
this.init = init;
|
this.init = init;
|
||||||
|
this.deinit = deinit;
|
||||||
this.injectPanel = injectPanel;
|
this.injectPanel = injectPanel;
|
||||||
this.mapping = mapping;
|
this.mapping = mapping;
|
||||||
|
this.addAudioMapping = addAudioMapping;
|
||||||
|
this.removeAudioMapping = removeAudioMapping;
|
||||||
|
this.addAudioOptions = addAudioOptions;
|
||||||
|
this.removeAudioOptions = removeAudioOptions;
|
||||||
|
this.AudioMappingOptions = AudioMappingOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,12 +146,37 @@ const Record = function(tp) {
|
||||||
recording: [],
|
recording: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
buffy.register(layerID);
|
||||||
|
// handle UI only if layer is selected
|
||||||
|
if (getLayer(layerID).isSelected()) {
|
||||||
const button = tp
|
const button = tp
|
||||||
.getPanelPropContainer(propTitle)
|
.getPanelPropContainer(propTitle)
|
||||||
.querySelector('.recordButton');
|
.querySelector('.recordButton');
|
||||||
if (button !== null) {
|
if (button !== null) {
|
||||||
button.classList.add('active');
|
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) => {
|
||||||
//if (isHot(layerID, propTitle)) {
|
//if (isHot(layerID, propTitle)) {
|
||||||
|
@ -181,21 +206,27 @@ const Record = function(tp) {
|
||||||
if(isRecording) {
|
if(isRecording) {
|
||||||
stopRecording();
|
stopRecording();
|
||||||
} else {
|
} else {
|
||||||
|
if (config.record.recordMapped) {
|
||||||
|
// make all mapped props hot and
|
||||||
Object.keys(audio.mapping)
|
Object.keys(audio.mapping)
|
||||||
.forEach((layerID) => {
|
.forEach((layerID) => {
|
||||||
if (getLayer(layerID).isSelected()) {
|
if (getLayer(layerID).isSelected()) {
|
||||||
Object.keys(audio.mapping[layerID])
|
Object.keys(audio.mapping[layerID])
|
||||||
.forEach((propTitle) => {
|
.forEach((propTitle) => {
|
||||||
makeHot(layerID, 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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -135,32 +135,53 @@ 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 propIsSequenced = isSequenced(propTitle);
|
||||||
|
const somethingToDo = sequenced !== propIsSequenced;
|
||||||
|
|
||||||
|
if (somethingToDo) {
|
||||||
const contextItem = sequenced ? 'sequence' : 'make static';
|
const contextItem = sequenced ? 'sequence' : 'make static';
|
||||||
const antiContextItem = sequenced ? 'make static' : 'sequence';
|
const antiContextItem = sequenced ? 'make static' : 'sequence';
|
||||||
|
|
||||||
const finishedSequencedEvent = (e) => {
|
const finishedSequencedEvent = (e) => {
|
||||||
tp.getPanel().removeEventListener('injected', finishedSequencedEvent);
|
// only care about events from our prop
|
||||||
console.log('debug FINISHED SEQUENCED EVENT', e, propTitle);
|
if (propTitle === e.detail.prop.join('.')) {
|
||||||
|
// if we un-sequence, we listen to stateEditors' event
|
||||||
|
if (!sequenced && e.detail.origin === 'stateEditors.ts' && e.detail.sequenced === sequenced) {
|
||||||
|
window.removeEventListener('sequenceEvent', finishedSequencedEvent);
|
||||||
resolve(true);
|
resolve(true);
|
||||||
|
|
||||||
|
// if we sequence, then we wait until the track is there
|
||||||
|
} else if (sequenced && e.detail.origin === 'BasicKeyframedTrack.tsx' && e.detail.sequenced === sequenced) {
|
||||||
|
window.removeEventListener('sequenceEvent', finishedSequencedEvent);
|
||||||
|
resolve(true);
|
||||||
|
} else {
|
||||||
|
console.log('TheatrePlayu::setSequenced', 'ignored event', e, e.detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const clickContextMenu = () => {
|
let counter = 0;
|
||||||
|
const clickContextMenu = (e) => {
|
||||||
let done = false;
|
let done = false;
|
||||||
tp.getPanelPropTitle(propTitle).removeEventListener('contextmenu', clickContextMenu);
|
if (e.target !== null) {
|
||||||
|
e.target.removeEventListener('contextmenu', clickContextMenu);
|
||||||
|
}
|
||||||
tp.shadowRoot.querySelectorAll('ul li span').forEach((s) => {
|
tp.shadowRoot.querySelectorAll('ul li span').forEach((s) => {
|
||||||
if (s.innerHTML.toLowerCase() === contextItem.toLowerCase()) {
|
if (s.innerHTML.toLowerCase() === contextItem.toLowerCase()) {
|
||||||
tp.getPanel().addEventListener('injected', finishedSequencedEvent);
|
window.addEventListener('sequenceEvent', finishedSequencedEvent);
|
||||||
s.click();
|
s.click();
|
||||||
console.log('debug click');
|
|
||||||
done = true;
|
done = true;
|
||||||
} else if (s.innerHTML.toLowerCase() === antiContextItem.toLowerCase()) {
|
} else if (s.innerHTML.toLowerCase() === antiContextItem.toLowerCase()) {
|
||||||
done = true;
|
done = true;
|
||||||
|
@ -169,14 +190,29 @@ const TheatrePlay = function(autoInit = false) {
|
||||||
});
|
});
|
||||||
if (!done) {
|
if (!done) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
clickContextMenu();
|
if (counter < 4) {
|
||||||
|
clickContextMenu(e);
|
||||||
|
counter++;
|
||||||
|
} else {
|
||||||
|
setSequenced(propTitle, sequenced, resolve);
|
||||||
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getPanelPropTitle(propTitle).addEventListener('contextmenu', clickContextMenu);
|
getPanelPropTitle(propTitle).addEventListener('contextmenu', clickContextMenu);
|
||||||
getPanelPropTitle(propTitle).dispatchEvent(new Event('contextmenu'));
|
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,37 +223,42 @@ 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.addEventListener('sequenceEvent', detectSE);
|
|
||||||
}, ms);// * promises.length);
|
|
||||||
})});
|
|
||||||
} else {
|
} else {
|
||||||
//console.error(k.path, 'did not find sequence button');
|
// we cannot select layers without pseudoclicking
|
||||||
// is (probably) already sequenced
|
// so let's wait for a happy 'injected' event that
|
||||||
|
// closes off the selection
|
||||||
|
//
|
||||||
|
// first, the listener callback
|
||||||
|
const f = () => {
|
||||||
|
tp.getPanel().removeEventListener('injected', f);
|
||||||
|
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 &&
|
||||||
|
@ -240,7 +281,8 @@ const TheatrePlay = function(autoInit = false) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!alreadyThere) {
|
if (!alreadyThere) {
|
||||||
promises.push(() => { return new Promise((subResolve) => {
|
promises.push(() => {
|
||||||
|
return new Promise((subResolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tp.sheet.sequence.position = keyframe.position;
|
tp.sheet.sequence.position = keyframe.position;
|
||||||
this.studio.transaction(({
|
this.studio.transaction(({
|
||||||
|
@ -249,16 +291,19 @@ const TheatrePlay = function(autoInit = false) {
|
||||||
set(prop, keyframe.value);
|
set(prop, keyframe.value);
|
||||||
subResolve();
|
subResolve();
|
||||||
});
|
});
|
||||||
}, ms);// * promises.length);
|
}, ms); // * promises.length);
|
||||||
})});
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
promises.push(() => { return new Promise((subResolve) => {
|
promises.push(() => {
|
||||||
|
return new Promise((subResolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tp.sheet.sequence.position = position;
|
tp.sheet.sequence.position = position;
|
||||||
subResolve();
|
subResolve();
|
||||||
}, ms);// * promises.length);
|
}, ms); // * promises.length);
|
||||||
})});
|
})
|
||||||
|
});
|
||||||
});
|
});
|
||||||
sequencialPromises(promises, resolve);
|
sequencialPromises(promises, resolve);
|
||||||
//Promise.all(promises).then(() => {
|
//Promise.all(promises).then(() => {
|
||||||
|
@ -273,18 +318,27 @@ 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)) {
|
||||||
|
waitify = true;
|
||||||
|
promises.push(() => {
|
||||||
|
return new Promise((subResolve) => {
|
||||||
setSequenced(propTitle, false)
|
setSequenced(propTitle, false)
|
||||||
.then(subResolve);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
Promise
|
|
||||||
.all(promises)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
subResolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sequencialPromises(promises, () => {
|
||||||
|
const timeout_ms = waitify ? 1000 : 0;
|
||||||
|
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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue