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;
};
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())) {
mapping[layer.id()] = {};
}
@ -85,6 +120,15 @@ const Audio = function(tp, record) {
}
if (!mapping[layer.id()].hasOwnProperty(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;
}
delete mapping[layer.id()][propTitle];
@ -94,21 +138,7 @@ const Audio = function(tp, record) {
return true;
}
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;
const createAudioOptions = (layer, propTitle, container) => {
const mappingOptions = mapping[layer.id()][propTitle];
const panel = tp.getPanel();
const audioOptions = document.createElement('div');
@ -117,7 +147,28 @@ const Audio = function(tp, record) {
audioOptions.classList.add(toCssClass(`audioOptions${propTitle}`));
audioOptions.style.position = 'relative';
audioOptions.style.width = '100%';
audioOptions.style.background = 'rgba(0,255,255,0.2)';
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.order = parseInt(container.style.order) + 1;
const updateMappingOptions = () => {
@ -171,7 +222,7 @@ const Audio = function(tp, record) {
sync_titleDom.innerHTML = 'sync with:';
sync_Dom.append(sync_titleDom);
audio_sync_options.forEach((o, oi) => {
audio_sync_options.forEach((o) => {
const sync_inputDom_label = document.createElement('label');
sync_inputDom_label.for = `audio_sync${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);
});
//removeAudioOptions();
container.after(audioOptions);
const audioButton = container.querySelector('.audioButton');
audioButton.classList.add('active');
canvass.push(fft_imgDom);
canvasCtxs.push(fft_imgDom.getContext("2d"));
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) => {
@ -261,9 +338,9 @@ const Audio = function(tp, record) {
// only selected layers have options
// otherwise the ui is not there
if (layer.isSelected()) {
const audioOptions = panel.querySelector(toCssClass(`audioOptions${propTitle}`,'.'));
if (audioOptions !== null) {
audioOptions.remove();
const audioOptions = panel.querySelectorAll(toCssClass(`audioOptions${propTitle}`,'.'));
if (audioOptions.length > 0) {
audioOptions.forEach((e) => { e.remove(); });
}
const audioButton = panel.querySelector(toCssClass(`audioButton${propTitle}`,'.'));
if (audioButton !== null) {
@ -400,7 +477,7 @@ const Audio = function(tp, record) {
// Distortion curve for the waveshaper, thanks to Kevin Ennis
// 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,
n_samples = 44100,
curve = new Float32Array(n_samples),
@ -479,7 +556,7 @@ const Audio = function(tp, record) {
console.log("getUserMedia not supported on your browser!");
}
function visualize() {
const visualize = () => {
const WIDTH = canvas.width;
const HEIGHT = canvas.height;
@ -632,18 +709,21 @@ const Audio = function(tp, record) {
};
});
Object.keys(values).forEach((layerID) => {
window.debugPreValues = clone(values[layerID]);
deFlattenObject(values[layerID]);
window.debugValues = clone(values[layerID]);
record.liveUpdater.immediateUpdate(getLayer(layerID), values[layerID]);
});
}
} else {
const position = tp.sheet.sequence.position;
propsToSet.forEach((p) => {
const title = tp
.getPanelPropTitle(p.title)
.parentNode.parentNode;
.getPanelPropTitle(p.title);
if (title !== null) {
const inputElement = title
.parentNode.parentNode
.querySelector('input.recording');
if (inputElement !== null) {
@ -651,6 +731,10 @@ const Audio = function(tp, record) {
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";
biquadFilter.gain.setTargetAtTime(0, audioCtx.currentTime, 0);

View file

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

View file

@ -49,7 +49,7 @@ const LiveBuffer = function() {
subValueBuffer.delete(value_time_s);
break;
}
};
}
}
});
}
@ -73,7 +73,7 @@ const LiveBuffer = function() {
let mergedValues = {};
let didMergeValues = {};
valueBuffer[id].forEach((value, value_time_s) => {
if (value_time_s < time_s) {
if (value_time_s <= time_s) {
mergedValues = {...mergedValues, ...value};
} else {
if (Object.keys(didMergeValues).length === 0) {
@ -116,7 +116,20 @@ const LiveUpdater = function(tp, buffy) {
}
};
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);
if (p !== false) {
const id = layer.id();
@ -152,8 +165,13 @@ const Record = function(tp) {
buffy.register(layerID);
// handle UI only if layer is selected
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
.getPanelPropTitle(propTitle)
.getPanelPropTitle(cPropTitle)
.parentNode.parentNode
.querySelector('.recordButton');
if (button !== null) {
@ -174,8 +192,13 @@ const Record = function(tp) {
}
// handle UI only if layer is selected
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
.getPanelPropTitle(propTitle)
.getPanelPropTitle(cPropTitle)
.parentNode.parentNode
.querySelector('.recordButton');
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 = () => {
console.log('Record::startRecording');
lastPositions = {};
tp.sheet.sequence.pause();
const layerKeys = Object.keys(hot);
layerKeys.forEach((layerID) => {
@ -324,22 +383,14 @@ const Record = function(tp) {
const input_clone = cloneInput(layerID, propTitle);
let lastPosition = buffy.NO_TIME;
if (input_clone !== null) {
input_clone.addEventListener('change', (e) => {
const position = tp.sheet.sequence.position;
const value = parseFloat(input_clone.value);
hot[layerID][propTitle].recording.push({
position,
value,
});
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;
});
//input_clone.addEventListener('change', () => {
//const position = tp.sheet.sequence.position;
//const value = parseFloat(input_clone.value);
//addValue(layerID, propTitle, value, position, lastPosition);
//const merged = getValue(layerID, position);
//liveUpdater.immediateUpdate(layer, merged);
//lastPosition = position;
//});
} else {
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
// and should be the layer anyways
uncloneInput(layerID, propTitle);
keyframes.push({
path: propTitle.split('.'),
keyframes: hot[layerID][propTitle].recording,
});
// 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({
path: propTitle.split('.'),
keyframes: hot[layerID][propTitle].recording,
});
}
});
//setTimeout(() => {
const kf = clone(keyframes);
promises.push(() => {
return new Promise((subResolve) => {
tp.setKeyframes(layer, keyframes).then(() => {
@ -404,6 +482,9 @@ const Record = function(tp) {
};
// public
this.addValue = addValue;
this.getValue = getValue;
this.liveUpdate = liveUpdate;
this.liveUpdater = liveUpdater;
this.addRecordButton = addRecordButton;
this.addHot = addHot;

View file

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