summaryrefslogtreecommitdiff
path: root/tools/mq_editor/viewer.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mq_editor/viewer.js')
-rw-r--r--tools/mq_editor/viewer.js38
1 files changed, 36 insertions, 2 deletions
diff --git a/tools/mq_editor/viewer.js b/tools/mq_editor/viewer.js
index c841acb..1ac1afd 100644
--- a/tools/mq_editor/viewer.js
+++ b/tools/mq_editor/viewer.js
@@ -54,7 +54,9 @@ class SpectrogramViewer {
this.partialSpectrumCanvas = document.getElementById('partialSpectrumCanvas');
this.partialSpectrumCtx = this.partialSpectrumCanvas ? this.partialSpectrumCanvas.getContext('2d') : null;
this._partialSpecCache = null; // {partialIndex, time, specData?} — see renderPartialSpectrum
+ this._partialRangeCache = null; // {partialIndex, dbMin, dbMax} — scanned across full partial duration
this.synthOpts = {}; // synth options forwarded to synthesizeMQ (forceResonator, etc.)
+ this.onGetSynthOpts = null; // callback() → opts; called before each spectrum compute
// Selection and editing
this.selectedPartial = -1;
@@ -178,6 +180,7 @@ class SpectrogramViewer {
selectPartial(index) {
this._partialSpecCache = null;
+ this._partialRangeCache = null;
this.selectedPartial = index;
this.render();
if (this.onPartialSelect) this.onPartialSelect(index);
@@ -690,7 +693,13 @@ class SpectrogramViewer {
const specData = this._computePartialSpectrum(partial, specTime);
this._partialSpecCache = {partialIndex: p, time: specTime, specData};
- const {squaredAmp, maxDB, sampleRate, fftSize} = specData;
+ // dB range: scanned across full partial duration, cached per partial
+ if (!this._partialRangeCache || this._partialRangeCache.partialIndex !== p) {
+ this._partialRangeCache = this._computePartialRange(p, partial);
+ }
+ const {dbMin: DB_MIN, dbMax: DB_MAX} = this._partialRangeCache;
+
+ const {squaredAmp, sampleRate, fftSize} = specData;
const numBins = fftSize / 2;
const binWidth = sampleRate / fftSize;
const color = this.partialColor(p);
@@ -709,7 +718,7 @@ class SpectrogramViewer {
for (let b = bStart; b <= bEnd; ++b) if (squaredAmp[b] > maxSq) maxSq = squaredAmp[b];
const magDB = 10 * Math.log10(Math.max(maxSq, 1e-20));
- const barH = Math.round(this.normalizeDB(magDB, maxDB) * (height - 12));
+ const barH = Math.round(Math.max(0, Math.min(1, (magDB - DB_MIN) / (DB_MAX - DB_MIN))) * (height - 12));
if (barH <= 0) continue;
const grad = ctx.createLinearGradient(0, height - barH, 0, height);
@@ -721,12 +730,19 @@ class SpectrogramViewer {
ctx.fillStyle = color;
ctx.fillText('P#' + p + ' @' + specTime.toFixed(3) + 's', 4, 10);
+
+ const amp = evalBezierAmp(curve, specTime);
+ ctx.fillStyle = '#f44';
+ ctx.textAlign = 'right';
+ ctx.fillText('A=' + amp.toFixed(3), width - 3, 10);
+ ctx.textAlign = 'left';
}
// Synthesise a 2048-sample Hann-windowed frame of `partial` centred on `time`, run FFT,
// return {squaredAmp, maxDB, sampleRate, fftSize}. Uses this.synthOpts (forceResonator etc).
// freqCurve times are shifted so synthesizeMQ's t=0 aligns with tStart = time − window/2.
_computePartialSpectrum(partial, time) {
+ if (this.onGetSynthOpts) this.synthOpts = this.onGetSynthOpts();
const sampleRate = this.audioBuffer.sampleRate;
const FFT_SIZE = 2048;
const windowDuration = FFT_SIZE / sampleRate;
@@ -771,6 +787,24 @@ class SpectrogramViewer {
return {squaredAmp, maxDB, sampleRate, fftSize: FFT_SIZE};
}
+ // Scan the partial across its full duration to find the peak dB level, then derive
+ // [dbMin, dbMax] as [peak − 60, peak]. Cached per partialIndex; only called once on select.
+ _computePartialRange(partialIndex, partial) {
+ const fc = partial.freqCurve;
+ if (!fc) return {partialIndex, dbMin: -60, dbMax: 0};
+ const N = 8;
+ let globalMaxSq = 1e-20;
+ for (let i = 0; i < N; ++i) {
+ const t = fc.t0 + (fc.t3 - fc.t0) * (i + 0.5) / N;
+ const {squaredAmp} = this._computePartialSpectrum(partial, t);
+ for (let b = 0; b < squaredAmp.length; ++b) {
+ if (squaredAmp[b] > globalMaxSq) globalMaxSq = squaredAmp[b];
+ }
+ }
+ const dbMax = 10 * Math.log10(globalMaxSq);
+ return {partialIndex, dbMin: dbMax - 60, dbMax};
+ }
+
// --- View management ---
updateViewBounds() {