simple generic recording, hook up audio

This commit is contained in:
jrkb 2023-09-28 16:41:11 +02:00
parent 50126f7cd4
commit 4cb5742afb
6 changed files with 200 additions and 18 deletions

View file

@ -145,7 +145,6 @@ const Audio = function(tp, record) {
const bb = fft_Dom.getBoundingClientRect(); const bb = fft_Dom.getBoundingClientRect();
const x = e.clientX - bb.x; const x = e.clientX - bb.x;
freq_down = mapValue(e.clientX, bb.x, bb.x + bb.width, 0, 256 * 8 / 2, true); freq_down = mapValue(e.clientX, bb.x, bb.x + bb.width, 0, 256 * 8 / 2, true);
console.log('up',JSON.parse(JSON.stringify(e)), e);
}); });
//removeAudioOptions(); //removeAudioOptions();
@ -182,8 +181,9 @@ const Audio = function(tp, record) {
console.log("Audio::addAudioButton", console.log("Audio::addAudioButton",
`impossible! cannot find panelPropContainer for ${propTitle}`); `impossible! cannot find panelPropContainer for ${propTitle}`);
} else if (container.querySelector('.audioButton') !== null) { } else if (container.querySelector('.audioButton') !== null) {
console.log("Audio::addAudioButton", // this is super verbose, let's not log by default
`already added an audio button for ${propTitle}`); //console.log("Audio::addAudioButton",
//`already added an audio button for ${propTitle}`);
} else { } else {
const button = document.createElement('div'); const button = document.createElement('div');
button.classList.add('audioButton'); button.classList.add('audioButton');
@ -478,6 +478,7 @@ const Audio = function(tp, record) {
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;
propsToSet.push({ propsToSet.push({
title: propTitle,
prop: layer.theatreObject.props[propTitle], prop: layer.theatreObject.props[propTitle],
value: m.value, value: m.value,
}); });
@ -487,6 +488,7 @@ const Audio = function(tp, record) {
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({
title: propTitle,
prop: layer.theatreObject.props[propTitle], prop: layer.theatreObject.props[propTitle],
value: m.value, value: m.value,
}); });
@ -501,6 +503,7 @@ const Audio = function(tp, record) {
} }
}); });
if (propsToSet.length > 0 && frameCount % 2 === 0) { if (propsToSet.length > 0 && frameCount % 2 === 0) {
if (!record.isRecording()) {
tp.studio.transaction(({ tp.studio.transaction(({
set set
}) => { }) => {
@ -508,14 +511,25 @@ const Audio = function(tp, record) {
set(p.prop, p.value, true); set(p.prop, p.value, true);
}); });
}); });
} else {
propsToSet.forEach((p) => {
// TODO: this does not have to be queried
// but we could store it in a map/set/dictionary/array/object
const inputElement = tp
.getPanelPropContainer(p.title)
.querySelector('input.recording');
if (inputElement !== null) {
inputElement.value = p.value;
inputElement.dispatchEvent(new Event('change'));
}
});
}
} }
const panel = tp.getPanel(); const panel = tp.getPanel();
const fft_images = panel.querySelectorAll('.audio_fft'); const fft_images = panel.querySelectorAll('.audio_fft');
if (fft_images !== null) { if (fft_images !== null) {
const src = canvas.toDataURL(); const src = canvas.toDataURL();
if (window.printDebug === true) {
console.log({canvas, src, fft_images, panel}, "DEBUG AUDIO");
}
fft_images.forEach((e) => { fft_images.forEach((e) => {
e.src = src; e.src = src;
}); });

View file

@ -85,6 +85,9 @@ const config = {
ignoreProps: ['transformOrigin', 'fontFamily', 'text', 'mirror_x', 'mirror_y', 'mirror_xy', 'fontVariationAxes', 'color'], ignoreProps: ['transformOrigin', 'fontFamily', 'text', 'mirror_x', 'mirror_y', 'mirror_xy', 'fontVariationAxes', 'color'],
defaultSmoothing: 0.7, defaultSmoothing: 0.7,
}, },
record: {
ignoreProps: ['fontVariationAxes','letterDelays','color'],
},
midi: { midi: {
touchTimeThreshold_s: 0.5, touchTimeThreshold_s: 0.5,
smoothingMix: 0.1, smoothingMix: 0.1,

View file

@ -55,6 +55,7 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
text: config.layer.defaultTexts[Math.floor(Math.random()*config.layer.defaultTexts.length)], text: config.layer.defaultTexts[Math.floor(Math.random()*config.layer.defaultTexts.length)],
}; };
let updateValuesViaTheatre = true;
let lastValues = {}; let lastValues = {};
let fontsHashMap = {}; let fontsHashMap = {};
let sequenceEventBuffer = []; let sequenceEventBuffer = [];
@ -204,6 +205,8 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
const onValuesChange = (values) => { const onValuesChange = (values) => {
if (values.dummy === true) if (values.dummy === true)
return; return;
if (!updateValuesViaTheatre)
return;
window.isRenderDirty = true; window.isRenderDirty = true;
if (Object.keys(values).length > 1) { if (Object.keys(values).length > 1) {
values.text = values.text.trim(); values.text = values.text.trim();
@ -792,11 +795,18 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
panel.addEventListener("mouseover", showBoundingBoxDivIfSelected); panel.addEventListener("mouseover", showBoundingBoxDivIfSelected);
panel.addEventListener("mouseleave", hideBoundingBoxDiv); panel.addEventListener("mouseleave", hideBoundingBoxDiv);
// should we have an audio object, let's inject the buttons, etc
if (typeof audio === 'object' && audio.hasOwnProperty('injectPanel')) { if (typeof audio === 'object' && audio.hasOwnProperty('injectPanel')) {
audio.injectPanel(this); audio.injectPanel(this);
} else { } else {
console.log('Layer::findInjectPanel', `cannot inject audio panel for ${this.id()} for some reason.`); console.log('Layer::findInjectPanel', `cannot inject audio panel for ${this.id()} for some reason.`);
} }
// should we have a record object, let's inject the buttons, etc
if (typeof record === 'object' && record.hasOwnProperty('injectPanel')) {
record.injectPanel(this);
} else {
console.log('Layer::findInjectPanel', `cannot inject record panel for ${this.id()} for some reason.`);
}
injectedPanel = true; injectedPanel = true;
const detail = {titles: Object.keys(panelPropTitles), containers: Object.keys(panelPropContainers)}; const detail = {titles: Object.keys(panelPropTitles), containers: Object.keys(panelPropContainers)};
@ -1036,6 +1046,9 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
return cppProps; return cppProps;
}; };
this.onValuesChange = onValuesChange; this.onValuesChange = onValuesChange;
this.updateValuesViaTheatre = (doIt) => {
updateValuesViaTheatre = doIt;
};
this.prepareForDepartureFromThisBeautifulExperience = () => { this.prepareForDepartureFromThisBeautifulExperience = () => {
this.hide(); this.hide();
hideBoundingBoxDiv(); hideBoundingBoxDiv();

View file

@ -72,8 +72,8 @@ const interactor = new Interactor();
const moduleFS = new ModuleFS(); const moduleFS = new ModuleFS();
window.moduleFS = moduleFS; window.moduleFS = moduleFS;
const record = new Record(tp); const record = new Record(tp);
window.debug_record = record; window.record = record;
const audio = new Audio(tp, record); // possibly nicer if we pass tp instead of attaching to window const audio = new Audio(tp, record);
window.audio = audio; window.audio = audio;
window.panelFinderTimeout = false; window.panelFinderTimeout = false;
@ -410,6 +410,10 @@ window.getLayers = () => {
return layers; return layers;
}; };
window.getLayer = (layerID) => {
return layers.find((layer) => layer.id() === layerID);
};
window.moveLayerUp = (layerID) => { window.moveLayerUp = (layerID) => {
layerOrder.moveUp(layerID); layerOrder.moveUp(layerID);
}; };

View file

@ -1,8 +1,9 @@
const Record = function(tp) { const Record = function(tp) {
const hot = {}; const hot = {};
let isRecording = false;
const addRecordButton = (layer, propTitle, isActive) => { const addRecordButton = (layer, propTitle) => {
const panel = tp.getPanel(); const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle); const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) { if (panelPropTitle !== null) {
@ -11,8 +12,9 @@ const Record = function(tp) {
console.log("Record::addRecordButton", console.log("Record::addRecordButton",
`impossible! cannot find panelPropContainer for ${propTitle}`); `impossible! cannot find panelPropContainer for ${propTitle}`);
} else if (container.querySelector('.recordButton') !== null) { } else if (container.querySelector('.recordButton') !== null) {
console.log("Record::addRecordButton", // this is super verbose, let's not log by default
`already added an record button for ${propTitle}`); //console.log("Record::addRecordButton",
//`already added an record button for ${propTitle}`);
} else { } else {
const button = document.createElement('div'); const button = document.createElement('div');
button.classList.add('recordButton'); button.classList.add('recordButton');
@ -24,9 +26,13 @@ const Record = function(tp) {
hot[layer.id()] = {}; hot[layer.id()] = {};
} }
if (!hot[layer.id()].hasOwnProperty(propTitle)) { if (!hot[layer.id()].hasOwnProperty(propTitle)) {
hot[layer.id()][propTitle] = {}; hot[layer.id()][propTitle] = {
recording: [],
};
button.classList.add('active'); button.classList.add('active');
startRecording();
} else { } else {
stopRecording();
delete hot[layer.id()][propTitle]; delete hot[layer.id()][propTitle];
if (Object.keys(hot[layer.id()]).length === 0) { if (Object.keys(hot[layer.id()]).length === 0) {
delete hot[layer.id()]; delete hot[layer.id()];
@ -41,8 +47,142 @@ const Record = function(tp) {
} }
}; };
const cloneInput = (layer, propTitle) => {
const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) {
const container = tp.getPanelPropContainer(panelPropTitle);
if (container === null) {
console.log("Record::cloneInput",
`impossible! cannot find panelPropContainer for ${propTitle}`);
} else if (container.querySelector('input.recording') !== null) {
console.log("Record::cloneInput",
`already cloned input for ${propTitle}`);
} else {
const input = container.querySelector('input');
if (input === null) {
console.log("Record::cloneInput",
`uuh.. seems there is no input to clone for ${propTitle}`);
} else {
const input_clone = input.cloneNode();
input_clone.classList.value = '';
input_clone.classList.add('recording');
input.parentNode.after(input_clone);
input.setAttribute('data-previousDisplay', input.style.display);
input.style.display = 'none';
return input_clone;
}
}
}
return null;
};
const uncloneInput = (layer, propTitle) => {
const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) {
const container = tp.getPanelPropContainer(panelPropTitle);
if (container === null) {
console.log("Record::uncloneInput",
`impossible! cannot find panelPropContainer for ${propTitle}`);
} else if (container.querySelector('input.recording') === null) {
console.log("Record::uncloneInput",
`already uncloned input for ${propTitle}`);
} else {
const input = container.querySelector('input:not(.recording)');
const input_clone = container.querySelector('input.recording');
if (input === null ) {
console.log("Record::uncloneInput",
`uuh.. seems there is no input for ${propTitle}`);
} else if (input_clone === null ) {
console.log("Record::uncloneInput",
`uuh.. seems there is no input_clone for ${propTitle}`);
} else {
input_clone.remove();
input.removeAttribute('data-previousDisplay');
const previousInputDisplay = input.getAttribute('data-previousDisplay');
input.style.display = previousInputDisplay;
}
}
}
};
const injectPanel = (layer) => {
const props = Object.keys(layer.theatreObject.value);
props.forEach((propTitle) => {
if (config.record.ignoreProps.indexOf(propTitle) < 0) {
addRecordButton(layer, propTitle);
}
});
};
const startRecording = () => {
tp.sheet.sequence.pause();
const layerKeys = Object.keys(hot);
layerKeys.forEach((layerID) => {
const layer = getLayer(layerID);
layer.updateValuesViaTheatre(false);
const propTitles = Object.keys(hot[layerID]);
propTitles.forEach((propTitle) => {
// NOTE: layerID is not actually used atm
// and should be the layer anyways
const input_clone = cloneInput(layerID, propTitle);
if (input_clone !== null) {
input_clone.addEventListener('change', (e) => {
hot[layerID][propTitle].recording.push({
position: tp.sheet.sequence.position,
value: parseFloat(input_clone.value),
});
});
} else {
console.log('whoops input_clone is null');
}
});
tp.sheet.sequence.position = 0;
tp.sheet.sequence.play();
});
isRecording = true;
};
const stopRecording = () => {
console.log('stoprecording');
const layerKeys = Object.keys(hot);
console.log({layerKeys});
layerKeys.forEach((layerID) => {
console.log(layerID);
const layer = getLayer(layerID);
layer.updateValuesViaTheatre(true);
const propTitles = Object.keys(hot[layerID]);
const keyframes = [];
propTitles.forEach((propTitle) => {
console.log(propTitle);
// NOTE: layerID is not actually used atm
// and should be the layer anyways
uncloneInput(layerID, propTitle);
console.log('should have uncloned input for ' + propTitle);
keyframes.push({
path: [propTitle],
keyframes: hot[layerID][propTitle].recording,
});
});
setTimeout(() => {
console.log('adding the keyframes now because we wnat it to happen right now please');
tp.addKeyframes(layer, keyframes);
}, 2000);
});
isRecording = false;
};
// public // public
this.addRecordButton = addRecordButton; this.addRecordButton = addRecordButton;
this.getHot = () => {
return hot;
};
this.isRecording = () => {
return isRecording;
};
this.injectPanel = injectPanel;
this.startRecording = startRecording;
this.stopRecording = stopRecording;
}; };
export { export {

View file

@ -571,10 +571,18 @@ const TheatrePlay = function(autoInit = false) {
} }
.audioButton{ .audioButton{
width: 20px; width: 20px;
margin: 2px;
} }
.audioButton.active{ .audioButton.active{
background: green; background: green;
} }
.recordButton{
width: 20px;
margin: 2px;
}
.recordButton.active{
background: green;
}
`; `;
this.shadowRoot.appendChild(style); this.shadowRoot.appendChild(style);
} }