Compare commits

...

2 commits

Author SHA1 Message Date
667447b9a2 add color with separate rgba 2023-10-08 15:57:29 +02:00
6ed4bd1227 separate createAudioOptions 2023-10-08 07:19:27 +02:00
4 changed files with 228 additions and 58 deletions

View file

@ -56,7 +56,42 @@ const Audio = function(tp, record) {
return true; return true;
}; };
const addAudioMapping = (layer, propTitle, options = new AudioMappingOptions()) => { const getAudioMappingOptions = (layer, propTitle) => {
if (propTitle === 'color') {
if (config.audio.colorSeparateRGBA) {
const r = new AudioMappingOptions();
const g = new AudioMappingOptions();
const b = new AudioMappingOptions();
const a = new AudioMappingOptions();
return [{r}, {g}, {b}, {a}];
} else {
const rgba = new AudioMappingOptions();
rgba.min_out = {r: 0, b: 0, g: 0, a: 0};
rgba.max_out = {r: 1, b: 1, g: 1, a: 1};
return rgba;
}
} else {
const o = new AudioMappingOptions();
// TODO: get min_out, max_out from layer.props
// check for typeof layer.props[propTitle.split('.')[0]] blabla
return o;
}
};
// potentially recursive
const addAudioMapping = (layer, propTitle, options = false) => {
if (!options) {
options = getAudioMappingOptions(layer, propTitle);
if (Array.isArray(options)) {
let isGood = true;
options.forEach((o) => {
const subPropKey = Object.keys(o)[0];
const subPropTitle = `${propTitle}.${subPropKey}`;
isGood = addAudioMapping(layer, subPropTitle, o[subPropKey]) ? isGood : false;
});
return isGood;
}
}
if (!mapping.hasOwnProperty(layer.id())) { if (!mapping.hasOwnProperty(layer.id())) {
mapping[layer.id()] = {}; mapping[layer.id()] = {};
} }
@ -85,6 +120,15 @@ const Audio = function(tp, record) {
} }
if (!mapping[layer.id()].hasOwnProperty(propTitle)) { if (!mapping[layer.id()].hasOwnProperty(propTitle)) {
// no propTitle // no propTitle
// perhaps color?
if (config.audio.separateRGBA && propTitle === 'color') {
let isGood = true;
isGood = removeAudioMapping(layer, 'color.r');
isGood = removeAudioMapping(layer, 'color.g');
isGood = removeAudioMapping(layer, 'color.b');
isGood = removeAudioMapping(layer, 'color.a');
return isGood;
}
return false; return false;
} }
delete mapping[layer.id()][propTitle]; delete mapping[layer.id()][propTitle];
@ -94,21 +138,7 @@ const Audio = function(tp, record) {
return true; return true;
} }
const addAudioOptions = (layer, propTitle) => { const createAudioOptions = (layer, propTitle, container) => {
if (!started) {
// audioOptions need a started init
init();
}
const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle === null) {
console.log('Audio::addAudioOptions::error',`cannot find panelPropTitle "${propTitle}"`);
return;
}
if (tp.getPanel().querySelector(toCssClass(`audioOptions${propTitle}`, '.')) !== null) {
//console.log('Audio::addAudioOptions::error',`audioOptions already exist for "${propTitle}"`);
return;
}
const container = panelPropTitle.parentNode.parentNode;
const mappingOptions = mapping[layer.id()][propTitle]; const mappingOptions = mapping[layer.id()][propTitle];
const panel = tp.getPanel(); const panel = tp.getPanel();
const audioOptions = document.createElement('div'); const audioOptions = document.createElement('div');
@ -117,7 +147,28 @@ const Audio = function(tp, record) {
audioOptions.classList.add(toCssClass(`audioOptions${propTitle}`)); audioOptions.classList.add(toCssClass(`audioOptions${propTitle}`));
audioOptions.style.position = 'relative'; audioOptions.style.position = 'relative';
audioOptions.style.width = '100%'; audioOptions.style.width = '100%';
if (propTitle.split('.')[0] === 'color' && propTitle.split('.').length > 1) {
switch(propTitle.split('.')[1]) {
case 'r': {
audioOptions.style.background = 'rgba(255,0,0,0.2)';
break;
}
case 'g': {
audioOptions.style.background = 'rgba(0,255,0,0.2)';
break;
}
case 'b': {
audioOptions.style.background = 'rgba(0,0,255,0.2)';
break;
}
case 'a': {
audioOptions.style.background = 'rgba(255,255,255,0.2)';
break;
}
}
} else {
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;
const updateMappingOptions = () => { const updateMappingOptions = () => {
@ -171,7 +222,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);
audio_sync_options.forEach((o, oi) => { audio_sync_options.forEach((o) => {
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;
@ -233,16 +284,42 @@ const Audio = function(tp, record) {
freq_down = mapValue(e.clientX, bb.x, bb.x + bb.width, 0, config.audio.fftBandsUsed, true); freq_down = mapValue(e.clientX, bb.x, bb.x + bb.width, 0, config.audio.fftBandsUsed, true);
}); });
//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();
return audioOptions;
};
const addAudioOptions = (layer, propTitle) => {
if (!started) {
// audioOptions need a started init
init();
}
const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle === null) {
console.log('Audio::addAudioOptions::error',`cannot find panelPropTitle "${propTitle}"`);
return;
}
if (tp.getPanel().querySelector(toCssClass(`audioOptions${propTitle}`, '.')) !== null) {
//console.log('Audio::addAudioOptions::error',`audioOptions already exist for "${propTitle}"`);
return;
}
const container = panelPropTitle.parentNode.parentNode;
if (propTitle === 'color' && config.audio.colorSeparateRGBA) {
// NOTE: attach reversed, because container.after(audioOptions)
createAudioOptions(layer, `${propTitle}.a`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
createAudioOptions(layer, `${propTitle}.b`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
createAudioOptions(layer, `${propTitle}.g`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
createAudioOptions(layer, `${propTitle}.r`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
} else {
createAudioOptions(layer, propTitle, container);
}
const audioButton = container.querySelector('.audioButton');
audioButton.classList.add('active');
}; };
const removeAudioOptions = (layer = false, propTitle = false) => { const removeAudioOptions = (layer = false, propTitle = false) => {
@ -261,9 +338,9 @@ const Audio = function(tp, record) {
// only selected layers have options // only selected layers have options
// otherwise the ui is not there // otherwise the ui is not there
if (layer.isSelected()) { if (layer.isSelected()) {
const audioOptions = panel.querySelector(toCssClass(`audioOptions${propTitle}`,'.')); const audioOptions = panel.querySelectorAll(toCssClass(`audioOptions${propTitle}`,'.'));
if (audioOptions !== null) { if (audioOptions.length > 0) {
audioOptions.remove(); audioOptions.forEach((e) => { e.remove(); });
} }
const audioButton = panel.querySelector(toCssClass(`audioButton${propTitle}`,'.')); const audioButton = panel.querySelector(toCssClass(`audioButton${propTitle}`,'.'));
if (audioButton !== null) { if (audioButton !== null) {
@ -400,7 +477,7 @@ const Audio = function(tp, record) {
// Distortion curve for the waveshaper, thanks to Kevin Ennis // Distortion curve for the waveshaper, thanks to Kevin Ennis
// http://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion // http://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion
function makeDistortionCurve(amount) { const makeDistortionCurve = (amount) => {
let k = typeof amount === "number" ? amount : 50, let k = typeof amount === "number" ? amount : 50,
n_samples = 44100, n_samples = 44100,
curve = new Float32Array(n_samples), curve = new Float32Array(n_samples),
@ -479,7 +556,7 @@ const Audio = function(tp, record) {
console.log("getUserMedia not supported on your browser!"); console.log("getUserMedia not supported on your browser!");
} }
function visualize() { const visualize = () => {
const WIDTH = canvas.width; const WIDTH = canvas.width;
const HEIGHT = canvas.height; const HEIGHT = canvas.height;
@ -632,18 +709,21 @@ const Audio = function(tp, record) {
}; };
}); });
Object.keys(values).forEach((layerID) => { Object.keys(values).forEach((layerID) => {
window.debugPreValues = clone(values[layerID]);
deFlattenObject(values[layerID]); deFlattenObject(values[layerID]);
window.debugValues = clone(values[layerID]);
record.liveUpdater.immediateUpdate(getLayer(layerID), values[layerID]); record.liveUpdater.immediateUpdate(getLayer(layerID), values[layerID]);
}); });
} }
} else { } else {
const position = tp.sheet.sequence.position;
propsToSet.forEach((p) => { propsToSet.forEach((p) => {
const title = tp const title = tp
.getPanelPropTitle(p.title) .getPanelPropTitle(p.title);
.parentNode.parentNode;
if (title !== null) { if (title !== null) {
const inputElement = title const inputElement = title
.parentNode.parentNode
.querySelector('input.recording'); .querySelector('input.recording');
if (inputElement !== null) { if (inputElement !== null) {
@ -651,6 +731,10 @@ const Audio = function(tp, record) {
inputElement.dispatchEvent(new Event('change')); inputElement.dispatchEvent(new Event('change'));
} }
} }
record.addValue(p.id, p.title, p.value, position);
if (!config.audio.colorSeparateRGBA || p.title === 'color.a') {
record.liveUpdate(p.layer, position);
}
}); });
} }
} }
@ -672,7 +756,7 @@ const Audio = function(tp, record) {
} }
} }
function voiceChange() { const voiceChange = () => {
distortion.oversample = "4x"; distortion.oversample = "4x";
biquadFilter.gain.setTargetAtTime(0, audioCtx.currentTime, 0); biquadFilter.gain.setTargetAtTime(0, audioCtx.currentTime, 0);

View file

@ -88,6 +88,7 @@ const config = {
fftBandsAnalysed: 256 * 8, fftBandsAnalysed: 256 * 8,
fftBandsUsed: 256 * 8 / 2, fftBandsUsed: 256 * 8 / 2,
fftHeight: 256 / 2, fftHeight: 256 / 2,
colorSeparateRGBA: true,
}, },
record: { record: {
ignoreProps: ['transformOrigin', 'fontFamily', 'text', 'mirror_x', 'mirror_y', 'mirror_xy'], ignoreProps: ['transformOrigin', 'fontFamily', 'text', 'mirror_x', 'mirror_y', 'mirror_xy'],

View file

@ -49,7 +49,7 @@ const LiveBuffer = function() {
subValueBuffer.delete(value_time_s); subValueBuffer.delete(value_time_s);
break; break;
} }
}; }
} }
}); });
} }
@ -73,7 +73,7 @@ const LiveBuffer = function() {
let mergedValues = {}; let mergedValues = {};
let didMergeValues = {}; let didMergeValues = {};
valueBuffer[id].forEach((value, value_time_s) => { valueBuffer[id].forEach((value, value_time_s) => {
if (value_time_s < time_s) { if (value_time_s <= time_s) {
mergedValues = {...mergedValues, ...value}; mergedValues = {...mergedValues, ...value};
} else { } else {
if (Object.keys(didMergeValues).length === 0) { if (Object.keys(didMergeValues).length === 0) {
@ -116,7 +116,20 @@ const LiveUpdater = function(tp, buffy) {
} }
}; };
this.immediateUpdate = (layer, values) => { this.immediateUpdate = (layer, values) => {
const v = {...layer.theatreObject.value, ...values}; const cv = clone(values);
if (cv.hasOwnProperty('color.r')) {
cv['color'] = {
r: cv['color.r'],
g: cv['color.g'],
b: cv['color.b'],
a: cv['color.a'],
};
delete cv['color.r'];
delete cv['color.g'];
delete cv['color.b'];
delete cv['color.a'];
}
const v = {...layer.theatreObject.value, ...cv};
const p = layer.values2cppProps(v); const p = layer.values2cppProps(v);
if (p !== false) { if (p !== false) {
const id = layer.id(); const id = layer.id();
@ -152,8 +165,13 @@ const Record = function(tp) {
buffy.register(layerID); buffy.register(layerID);
// handle UI only if layer is selected // handle UI only if layer is selected
if (getLayer(layerID).isSelected()) { if (getLayer(layerID).isSelected()) {
let cPropTitle = clone(propTitle);
// if colors are separate, there is still just one button
if (cPropTitle.indexOf('color.') === 0) {
cPropTitle = 'color';
}
const button = tp const button = tp
.getPanelPropTitle(propTitle) .getPanelPropTitle(cPropTitle)
.parentNode.parentNode .parentNode.parentNode
.querySelector('.recordButton'); .querySelector('.recordButton');
if (button !== null) { if (button !== null) {
@ -174,8 +192,13 @@ const Record = function(tp) {
} }
// handle UI only if layer is selected // handle UI only if layer is selected
if (getLayer(layerID).isSelected()) { if (getLayer(layerID).isSelected()) {
let cPropTitle = clone(propTitle);
// if colors are separate, there is still just one button
if (cPropTitle.indexOf('color.') === 0) {
cPropTitle = 'color';
}
const button = tp const button = tp
.getPanelPropTitle(propTitle) .getPanelPropTitle(cPropTitle)
.parentNode.parentNode .parentNode.parentNode
.querySelector('.recordButton'); .querySelector('.recordButton');
if (button !== null) { if (button !== null) {
@ -310,8 +333,44 @@ const Record = function(tp) {
}); });
}; };
let lastPositions = {};
const addValue = (layerID,
propTitle,
value,
position = tp.sheet.sequence.position,
lastPosition = buffy.NO_TIME) => {
hot[layerID][propTitle].recording.push({
position,
value,
});
const recording = {
[propTitle]: value,
};
if (!lastPositions.hasOwnProperty(layerID)) {
lastPositions[layerID] = {};
}
if (lastPosition === buffy.NO_TIME) {
if (!lastPositions[layerID].hasOwnProperty(propTitle)) {
lastPositions[layerID][propTitle] = position;
}
} else {
lastPositions[layerID][propTitle] = lastPosition;
}
buffy.addValues(layerID, recording, position, lastPositions[layerID][propTitle]);
};
const getValue = (layerID, position) => {
const merged = clone(buffy.getValues(layerID, position));
deFlattenObject(merged);
return merged;
};
const liveUpdate = (layer, position = tp.sheet.sequence.position) => {
const merged = getValue(layer.id(), position);
liveUpdater.immediateUpdate(layer, merged);
};
const startRecording = () => { const startRecording = () => {
console.log('Record::startRecording'); console.log('Record::startRecording');
lastPositions = {};
tp.sheet.sequence.pause(); tp.sheet.sequence.pause();
const layerKeys = Object.keys(hot); const layerKeys = Object.keys(hot);
layerKeys.forEach((layerID) => { layerKeys.forEach((layerID) => {
@ -324,22 +383,14 @@ const Record = function(tp) {
const input_clone = cloneInput(layerID, propTitle); const input_clone = cloneInput(layerID, propTitle);
let lastPosition = buffy.NO_TIME; let lastPosition = buffy.NO_TIME;
if (input_clone !== null) { if (input_clone !== null) {
input_clone.addEventListener('change', (e) => { //input_clone.addEventListener('change', () => {
const position = tp.sheet.sequence.position; //const position = tp.sheet.sequence.position;
const value = parseFloat(input_clone.value); //const value = parseFloat(input_clone.value);
hot[layerID][propTitle].recording.push({ //addValue(layerID, propTitle, value, position, lastPosition);
position, //const merged = getValue(layerID, position);
value, //liveUpdater.immediateUpdate(layer, merged);
}); //lastPosition = position;
const recording = { //});
[propTitle]: value,
};
buffy.addValues(layerID, recording, position, lastPosition);
const merged = clone(buffy.getValues(layerID, position));
deFlattenObject(merged);
liveUpdater.immediateUpdate(layer, merged);
lastPosition = position;
});
} else { } else {
console.log('Record::startRecording', `whoops input_clone for ${propTitle} is null`); console.log('Record::startRecording', `whoops input_clone for ${propTitle} is null`);
} }
@ -373,12 +424,39 @@ const Record = function(tp) {
// 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);
// special treatment if we have sperate RGBA for color
if (propTitle.indexOf('color.') === 0) {
if (propTitle === 'color.r') {
const recording = [];
hot[layerID]['color.r'].recording.forEach((rr, ri) => {
if (ri < hot[layerID]['color.r'].recording.length &&
ri < hot[layerID]['color.g'].recording.length &&
ri < hot[layerID]['color.b'].recording.length &&
ri < hot[layerID]['color.a'].recording.length) {
const r = clone(rr);
r.value = {
r: hot[layerID]['color.r'].recording[ri].value,
g: hot[layerID]['color.g'].recording[ri].value,
b: hot[layerID]['color.b'].recording[ri].value,
a: hot[layerID]['color.a'].recording[ri].value,
};
recording.push(r);
}
});
keyframes.push({
path: ['color'],
keyframes: recording,
});
}
} else {
keyframes.push({ keyframes.push({
path: propTitle.split('.'), path: propTitle.split('.'),
keyframes: hot[layerID][propTitle].recording, keyframes: hot[layerID][propTitle].recording,
}); });
}
}); });
//setTimeout(() => { //setTimeout(() => {
const kf = clone(keyframes);
promises.push(() => { promises.push(() => {
return new Promise((subResolve) => { return new Promise((subResolve) => {
tp.setKeyframes(layer, keyframes).then(() => { tp.setKeyframes(layer, keyframes).then(() => {
@ -404,6 +482,9 @@ const Record = function(tp) {
}; };
// public // public
this.addValue = addValue;
this.getValue = getValue;
this.liveUpdate = liveUpdate;
this.liveUpdater = liveUpdater; this.liveUpdater = liveUpdater;
this.addRecordButton = addRecordButton; this.addRecordButton = addRecordButton;
this.addHot = addHot; this.addHot = addHot;

View file

@ -347,7 +347,7 @@ function verifyVariableTimeProject(vt_project) {
function clone(a) { function clone(a) {
return JSON.parse(JSON.stringify(a)); return JSON.parse(JSON.stringify(a));
}; }
function getParents(elem, until = null) { function getParents(elem, until = null) {
const parents = []; const parents = [];
@ -461,7 +461,11 @@ const deFlattenObject = (o, ignoreKeys = [], pathSymbol = '.') => {
for (let i = ks.length - 1; i > 0; i--) { for (let i = ks.length - 1; i > 0; i--) {
sos = {[ks[i]]: sos}; sos = {[ks[i]]: sos};
} }
if (typeof o[ks[0]] === 'object') {
o[ks[0]] = {...o[ks[0]], ...sos};
} else {
o[ks[0]] = sos; o[ks[0]] = sos;
}
delete o[k]; delete o[k];
} }
} }