From 8075cf2b168c5620bc770feef0cd0c63a806636c Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sat, 13 Apr 2024 18:03:44 +0200 Subject: [PATCH] save and load audioLayer and audioPlayer dependencies hashes: openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501 ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3 ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239 ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8 theatre c60e63f5be4466e21b6bf06f6580f6100687306b --- bin/em/variabletime/web/js/audio.js | 2 +- bin/em/variabletime/web/js/audioLayer.js | 82 ++++++++++++++++---- bin/em/variabletime/web/js/audioPlayer.js | 2 +- bin/em/variabletime/web/js/main.js | 2 +- bin/em/variabletime/web/js/record.js | 8 ++ bin/em/variabletime/web/js/theatre-play.js | 88 ++++++++++++++++++---- bin/em/variabletime/web/js/utils.js | 20 +++++ 7 files changed, 172 insertions(+), 32 deletions(-) diff --git a/bin/em/variabletime/web/js/audio.js b/bin/em/variabletime/web/js/audio.js index 8ee4375..8af7e4d 100644 --- a/bin/em/variabletime/web/js/audio.js +++ b/bin/em/variabletime/web/js/audio.js @@ -1162,7 +1162,7 @@ const Audio = function(tp, record) { } }); } else { - console.log("getUserMedia not supported on your browser!"); + console.log("Audio::init","getUserMedia not supported on your browser!"); } const visualize = () => { diff --git a/bin/em/variabletime/web/js/audioLayer.js b/bin/em/variabletime/web/js/audioLayer.js index 2fc67b8..b9759ad 100644 --- a/bin/em/variabletime/web/js/audioLayer.js +++ b/bin/em/variabletime/web/js/audioLayer.js @@ -1,5 +1,6 @@ import { - sanitizeTheatreKey + sanitizeTheatreKey, + arraysEqual, } from './utils.js'; const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { @@ -14,14 +15,34 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { props[Object.keys(values)[0]] = tp.core.types.boolean(false); } + const onValuesChangeCallbacks = []; + // private functions // const onValuesChange = (values) => { console.log(values); + const n_callbacks = onValuesChangeCallbacks.length; + const successes = []; + if (n_callbacks > 0) { + for (let i = 0; i < n_callbacks; i++) { + if (typeof onValuesChangeCallbacks[i] === 'function') { + if(onValuesChangeCallbacks[i](values)) { + successes.unshift(i); + } + } else { + console.log('AudioLayer::onValuesChange', 'holy shit, the callback is not a function'); + } + } + successes.forEach((i) => { + onValuesChangeCallbacks.splice(i, 1); + }); + } }; const findInjectPanel = () => { - console.log('find and inject'); + console.log('AudioLayer::findInjectPanel'); + const sequencePanel = tp.getSequencePanel(); + const sequencePanelLeft = tp.getSequencePanelLeft(); }; const setTime = (filename, start, stop) => { @@ -104,13 +125,33 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { values[filename] = false; props[filename] = tp.core.types.boolean(values[filename]); this.props = props; - tp.changeObject(audioLayerID, { - dummy: true + + const setDummy = () => { + tp.changeObject(audioLayerID, { + dummy: true + }); + }; + + onValuesChangeCallbacks.push((changedValues) => { + if (changedValues.dummy) { + // NOTE: any idea how to avoid this ugly nesting? + onValuesChangeCallbacks.push((againChangedValues) => { + const expectedValueKeys = Object.keys(values); + const givenValueKeys = Object.keys(againChangedValues); + if (arraysEqual(expectedValueKeys, givenValueKeys)) { + resolve(); + return true; + } else { + return false; + } + }); + tp.changeObject(audioLayerID, this.props); + return true; + } else { + return false; + } }); - setTimeout(() => { - tp.changeObject(audioLayerID, this.props); - resolve(); - }, 100); + setDummy(); }); }; this.removeFile = (filename) => { @@ -120,13 +161,26 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { delete values[filename]; delete props[filename]; this.props = props; - tp.changeObject(audioLayerID, { - dummy: true + const setDummy = () => { + tp.changeObject(audioLayerID, { + dummy: true + }); + }; + + onValuesChangeCallbacks.push((changedValues) => { + if (changedValues.dummy) { + // NOTE: any idea how to avoid this ugly nesting? + onValuesChangeCallbacks.push((againChangedValues) => { + const expectedValueKeys = Object.keys(values); + const givenValueKeys = Object.keys(againChangedValues); + if (arraysEqual(expectedValueKeys, givenValueKeys)) { + resolve(); + } + }); + tp.changeObject(audioLayerID, this.props); + } }); - setTimeout(() => { - tp.changeObject(audioLayerID, this.props); - resolve(); - }, 100); + setDummy(); }); }; this.setTime = setTime; diff --git a/bin/em/variabletime/web/js/audioPlayer.js b/bin/em/variabletime/web/js/audioPlayer.js index 802b70a..d3c7ee2 100644 --- a/bin/em/variabletime/web/js/audioPlayer.js +++ b/bin/em/variabletime/web/js/audioPlayer.js @@ -30,7 +30,7 @@ const AudioPlayer = function() { this.addMany = (manyAudioElements) => { manyAudioElements.forEach((e) => { - this.add(e.audioID, e.layerID, e.propTitle, e.file); + this.add(e.audioID, e.layerID, e.propTitle, e.file, e.startTime); }); }; diff --git a/bin/em/variabletime/web/js/main.js b/bin/em/variabletime/web/js/main.js index 04d2c07..60ca1ee 100644 --- a/bin/em/variabletime/web/js/main.js +++ b/bin/em/variabletime/web/js/main.js @@ -604,7 +604,7 @@ const addAudioLayer = (filename = false) => { }); }; -const addExistingAudioLayer = (audioLayerID, values) => { +const addExistingAudioLayer = (audioLayerID, values = false) => { return new Promise((resolve) => { const audioLayer = new AudioLayer(tp, audioLayerID, values, false); audioLayers.push(audioLayer); diff --git a/bin/em/variabletime/web/js/record.js b/bin/em/variabletime/web/js/record.js index fcb729f..9afe05d 100644 --- a/bin/em/variabletime/web/js/record.js +++ b/bin/em/variabletime/web/js/record.js @@ -248,6 +248,14 @@ const Record = function(tp) { if (cPropTitle.indexOf('color.') === 0) { cPropTitle = 'color'; } + const propTitleDom = tp.getPanelPropTitle(cPropTitle); + if (propTitleDom === null) { + // whoops, probably currently transitioning, + // so it will not be necessary to remove the class + // when it will be recreated on demand, + // it won't have the class anyways. + return; + } const button = tp .getPanelPropTitle(cPropTitle) .parentNode.parentNode diff --git a/bin/em/variabletime/web/js/theatre-play.js b/bin/em/variabletime/web/js/theatre-play.js index ad7d62d..68eb14f 100644 --- a/bin/em/variabletime/web/js/theatre-play.js +++ b/bin/em/variabletime/web/js/theatre-play.js @@ -23,6 +23,10 @@ const TheatrePlay = function(autoInit = false) { const theatreObjects = {}; let theatrePanel = null; let sequencePanelLeft = null; + const getSequencePanel = () => { + sequencePanelLeft = tp.shadowRoot.querySelector('[data-testid="SequencePanel-Object"]'); + return sequencePanelLeft; + }; const getSequencePanelLeft = () => { sequencePanelLeft = tp.shadowRoot.querySelector('[data-testid="SequencePanel-Left"]'); return sequencePanelLeft; @@ -321,6 +325,7 @@ const TheatrePlay = function(autoInit = false) { }; this.isSequenced = isSequenced; this.getSequenceButton = getSequenceButton; + this.getSequencePanel = getSequencePanel; this.getSequencePanelLeft = getSequencePanelLeft; this.getPanel = getPanel; this.getPanelPropTitle = getPanelPropTitle; @@ -589,6 +594,28 @@ const TheatrePlay = function(autoInit = false) { audioMapping: audio.getMapping(), }; const theatre = tp.studio.createContentOfSaveFile(currentProjectId); + // yeaaaah we don't want to fuck with theatre's saveFile stuff + // let's do this ourselves + const audioLayers = getAudioLayers(); + if (audioLayers.length > 0) { + vt_params['audioLayers'] = {}; + audioLayers.forEach((audioLayer) => { + vt_params['audioLayers'][audioLayer.id()] = getKeyframes(audioLayer); + }); + const audioPlayerElements = audio + .audioPlayer + .audioElements + .map((e) => { + return { + audioID: e.audioID, + layerID: e.layerID, + propTitle: e.propTitle, + file: e.file, + startTime: e.startTime + }; + }); + vt_params['audioPlayerElements'] = audioPlayerElements; + } return this.theatre2variableTime(theatre, vt_params); }; this.saveProject = (projectId = project.address.projectId, vt_project = false, silent = true) => { @@ -709,12 +736,12 @@ const TheatrePlay = function(autoInit = false) { }) => { set(window.getArtboard().theatreObject.props, artboardProps); }); - if (project.hasOwnProperty('audioMapping')) { - audio.setMapping(project.audioMapping); - } - if (project.hasOwnProperty('audioSavedMapping')) { - audio.setSavedMapping(project.audioSavedMapping); - } + if (project.hasOwnProperty('audioMapping')) { + audio.setMapping(project.audioMapping); + } + if (project.hasOwnProperty('audioSavedMapping')) { + audio.setSavedMapping(project.audioSavedMapping); + } // load layers const layerPromises = []; @@ -723,17 +750,48 @@ const TheatrePlay = function(autoInit = false) { .forEach((layerId) => { window.setLoadingTask(`setting up the shapes of ${layerId} to come`, 90); window.project_fontsHashMap = project.fontsHashMap; - layerPromises.push(window.addExistingLayer(layerId, objects[layerId])); + //layerPromises.push(window.addExistingLayer(layerId, objects[layerId])); + layerPromises.push(() => { + return new Promise((r) => { + window.addExistingLayer(layerId, objects[layerId]).then(() => { + r(); + }); + }); + }); }); - //console.log(clone(objects)); - Object.keys(objects) - .filter((e) => e.indexOf('audio-') === 0) - .forEach((layerId) => { - window.setLoadingTask(`setting up the sounds of ${layerId} to come`, 90); - window.project_fontsHashMap = project.fontsHashMap; - layerPromises.push(window.addExistingAudioLayer(layerId, objects[layerId])); + if (typeof project['audioPlayerElements'] === 'object') { + audio.audioPlayer.addMany(project['audioPlayerElements']); + } + if (typeof project['audioLayers'] === 'object') { + const audioLayerIDs = Object.keys(project['audioLayers']); + audioLayerIDs.forEach((audioLayerID) => { + layerPromises.push(() => { + window.setLoadingTask(`setting up the sounds of ${audioLayerID} to come`, 90); + return new Promise((r) => { + window.addExistingAudioLayer(audioLayerID).then((audioLayer) => { + const promises = []; + project['audioLayers'][audioLayerID].forEach((keyframeCombo) => { + const filename = keyframeCombo.path.join(''); + promises.push(() => { + return new Promise((rr) => { + audioLayer.addFile(filename) + .then(() => { + rr(); // resolve + }); + + }); + }); + }); + sequencialPromises(promises, () => { + tp.addKeyframes(audioLayer, project['audioLayers'][audioLayerID]); + r(); // resolve + }); + }); + }); + }); }); - Promise.all(layerPromises).then(() => { + } + sequencialPromises(layerPromises, () => { window.layerOrder.set(project.layerOrder); this.duration = this.core.val(this.sheet.sequence.pointer.length); if (project.layerOrder.length > 0) { diff --git a/bin/em/variabletime/web/js/utils.js b/bin/em/variabletime/web/js/utils.js index 93d8e98..8fe1a20 100644 --- a/bin/em/variabletime/web/js/utils.js +++ b/bin/em/variabletime/web/js/utils.js @@ -531,6 +531,25 @@ const getNestedProperty = (o, a, verify = false) => { return o[b[0]]; }; +const findNestedObjectKey = (o, k, exact = true, history = [], result = []) => { + const oks = Object.keys(o); + for (let i = 0; i < oks.length; i++) { + if (k === oks[i] || (!exact && oks[i].indexOf(k) >= 0)) { + history.push(oks[i]); + result.push({found: o[oks[i]], history}); + } + if (typeof o[oks[i]] === 'object') { + const ok = Object.keys(o[oks[i]]); + const ok_history = clone(history); + ok_history.push(oks[i]); + findNestedObjectKey(o[oks[i]], k, exact, ok_history, result); + } + } + if (history.length === 0) { + return result; + } +}; + // NOTE: all input values are range 0-1 const rgbaToHexa = (rgba) => { return '#' + ( @@ -610,6 +629,7 @@ export { flattenObject, deFlattenObject, getNestedProperty, + findNestedObjectKey, rgbaToHexa, hexaToRgba, getFileExtensionFromMimeType,