diff options
Diffstat (limited to 'tools/mq_editor/app.js')
| -rw-r--r-- | tools/mq_editor/app.js | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/tools/mq_editor/app.js b/tools/mq_editor/app.js index 90f8f7e..862ec6c 100644 --- a/tools/mq_editor/app.js +++ b/tools/mq_editor/app.js @@ -46,6 +46,45 @@ let currentSource = null; let extractedPartials = null; let stftCache = null; +// Undo/redo +const undoStack = []; +const redoStack = []; + +function _updateUndoRedoBtns() { + document.getElementById('undoBtn').disabled = undoStack.length === 0; + document.getElementById('redoBtn').disabled = redoStack.length === 0; +} + +function pushUndo() { + undoStack.push(JSON.parse(JSON.stringify(extractedPartials || []))); + redoStack.length = 0; + if (undoStack.length > 50) undoStack.shift(); + _updateUndoRedoBtns(); +} + +function _applySnapshot(snap) { + extractedPartials = snap; + editor.setPartials(snap); + if (viewer) { + viewer.setPartials(snap); + viewer.setKeepCount(snap.length > 0 ? getKeepCount() : 0); + viewer.selectPartial(-1); + } + _updateUndoRedoBtns(); +} + +function undo() { + if (!undoStack.length) return; + redoStack.push(JSON.parse(JSON.stringify(extractedPartials || []))); + _applySnapshot(undoStack.pop()); +} + +function redo() { + if (!redoStack.length) return; + undoStack.push(JSON.parse(JSON.stringify(extractedPartials || []))); + _applySnapshot(redoStack.pop()); +} + const wavFile = document.getElementById('wavFile'); const chooseFileBtn = document.getElementById('chooseFileBtn'); const extractBtn = document.getElementById('extractBtn'); @@ -83,6 +122,7 @@ editor.onPartialDeleted = () => { if (viewer && extractedPartials) viewer.setKeepCount(extractedPartials.length > 0 ? getKeepCount() : 0); }; +editor.onBeforeChange = pushUndo; // Initialize audio context function initAudioContext() { @@ -111,6 +151,7 @@ function loadAudioBuffer(buffer, label) { editor.setViewer(viewer); viewer.onPartialSelect = (i) => editor.onPartialSelect(i); viewer.onRender = () => editor.onRender(); + viewer.onBeforeChange = pushUndo; if (label.startsWith('Test WAV')) validateTestWAVPeaks(stftCache); }, 10); } @@ -213,6 +254,8 @@ function runExtraction() { } extractBtn.disabled = false; autoSpreadAllBtn.disabled = false; + document.getElementById('newPartialBtn').disabled = false; + undoStack.length = 0; redoStack.length = 0; _updateUndoRedoBtns(); }, 50); } @@ -221,6 +264,36 @@ extractBtn.addEventListener('click', () => { runExtraction(); }); +function createNewPartial() { + if (!audioBuffer || !extractedPartials) return; + pushUndo(); + const dur = audioBuffer.duration; + const newPartial = { + times: [0, dur], + freqs: [440, 440], + amps: [1.0, 1.0], + phases: [0, 0], + muted: false, + freqCurve: { + t0: 0, t1: dur / 3, t2: dur * 2 / 3, t3: dur, + v0: 440, v1: 440, v2: 440, v3: 440, + a0: 1.0, a1: 1.0, a2: 1.0, a3: 1.0, + }, + replicas: { decay_alpha: 0.1, jitter: 0.05, spread_above: 0.02, spread_below: 0.02 }, + }; + extractedPartials.unshift(newPartial); + editor.setPartials(extractedPartials); + if (viewer) { + viewer.setPartials(extractedPartials); + viewer.setKeepCount(getKeepCount()); + viewer.selectPartial(0); + } +} + +document.getElementById('newPartialBtn').addEventListener('click', createNewPartial); +document.getElementById('undoBtn').addEventListener('click', undo); +document.getElementById('redoBtn').addEventListener('click', redo); + autoSpreadAllBtn.addEventListener('click', () => { if (!extractedPartials || !stftCache) return; const fs = stftCache.fftSize; @@ -353,6 +426,13 @@ function playSynthesized() { // Keyboard shortcuts document.addEventListener('keydown', (e) => { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return; + if (e.ctrlKey && e.code === 'KeyZ' && !e.shiftKey) { + e.preventDefault(); undo(); return; + } else if (e.ctrlKey && (e.code === 'KeyY' || (e.code === 'KeyZ' && e.shiftKey))) { + e.preventDefault(); redo(); return; + } else if (e.code === 'KeyN' && !e.ctrlKey && !e.metaKey) { + e.preventDefault(); createNewPartial(); return; + } if (e.code === 'Digit1') { e.preventDefault(); playSynthesized(); |
