summaryrefslogtreecommitdiff
path: root/tools/mq_editor/app.js
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-18 22:59:15 +0100
committerskal <pascal.massimino@gmail.com>2026-02-18 22:59:15 +0100
commitcd771a49d1d59b1403ef7f358398fa2f0f646cc4 (patch)
tree512ca89f54e3a92f65f7d1a7c51193c461f5c23a /tools/mq_editor/app.js
parent080f457040ca54256325b922ebd67cde5c0dc030 (diff)
feat(mq_editor): replace replicas with harmonics model
- Fundamental f0 always synthesized; harmonics added at n*freq_mult - decay^n amplitude rolloff per harmonic (capped at 0.90) - Resonator mode also expanded across harmonics (per-harmonic y1/y2 state) - UI: h.decay, h.freq (default 2.0), jitter, spread↑/↓ params - Viewer: faint dotted harmonic bands with spread visualization - Default freq_mult=2.0 (natural harmonic series) handoff(Gemini): harmonics model complete, ready for next task Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'tools/mq_editor/app.js')
-rw-r--r--tools/mq_editor/app.js14
1 files changed, 8 insertions, 6 deletions
diff --git a/tools/mq_editor/app.js b/tools/mq_editor/app.js
index 1e17adf..9df00fb 100644
--- a/tools/mq_editor/app.js
+++ b/tools/mq_editor/app.js
@@ -220,7 +220,9 @@ function loadAudioBuffer(buffer, label) {
if (!extractedPartials) extractedPartials = [];
pushUndo();
const {spread_above, spread_below} = autodetectSpread(partial, stftCache, fftSize, audioBuffer.sampleRate);
- partial.replicas = { ...partial.replicas, spread_above, spread_below };
+ if (!partial.harmonics) partial.harmonics = { decay: 0.0, freq_mult: 2.0, jitter: 0.05, spread_above: 0.02, spread_below: 0.02 };
+ partial.harmonics.spread_above = spread_above;
+ partial.harmonics.spread_below = spread_below;
extractedPartials.unshift(partial);
refreshPartialsView(0);
setStatus(`${exploreMode}: added partial (${extractedPartials.length} total)`, 'info');
@@ -331,7 +333,7 @@ function createNewPartial() {
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 },
+ harmonics: { decay: 0.0, freq_mult: 2.0, jitter: 0.05, spread_above: 0.02, spread_below: 0.02 },
};
extractedPartials.unshift(newPartial);
refreshPartialsView(0);
@@ -355,12 +357,12 @@ function autoSpreadAll() {
if (!extractedPartials || !stftCache) return;
const fs = stftCache.fftSize;
const sr = audioBuffer.sampleRate;
- const defaults = { decay_alpha: 0.1, jitter: 0.05, spread_above: 0.02, spread_below: 0.02 };
+ const defaults = { decay: 0.0, freq_mult: 2.0, jitter: 0.05, spread_above: 0.02, spread_below: 0.02 };
for (const p of extractedPartials) {
const {spread_above, spread_below} = autodetectSpread(p, stftCache, fs, sr);
- if (!p.replicas) p.replicas = { ...defaults };
- p.replicas.spread_above = spread_above;
- p.replicas.spread_below = spread_below;
+ if (!p.harmonics) p.harmonics = { ...defaults };
+ p.harmonics.spread_above = spread_above;
+ p.harmonics.spread_below = spread_below;
}
if (viewer) viewer.render();
const sel = viewer ? viewer.selectedPartial : -1;