summaryrefslogtreecommitdiff
path: root/tools/mq_editor/mq_extract.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mq_editor/mq_extract.js')
-rw-r--r--tools/mq_editor/mq_extract.js38
1 files changed, 38 insertions, 0 deletions
diff --git a/tools/mq_editor/mq_extract.js b/tools/mq_editor/mq_extract.js
index 2293d52..8a0ea0e 100644
--- a/tools/mq_editor/mq_extract.js
+++ b/tools/mq_editor/mq_extract.js
@@ -16,6 +16,9 @@ function extractPartials(params, stftCache) {
const partials = trackPartials(frames);
+ // Second pass: extend partials leftward to recover onset frames
+ expandPartialsLeft(partials, frames);
+
for (const partial of partials) {
partial.freqCurve = fitBezier(partial.times, partial.freqs);
partial.ampCurve = fitBezier(partial.times, partial.amps);
@@ -144,6 +147,41 @@ function trackPartials(frames) {
return partials;
}
+// Second pass: extend each partial leftward to recover onset frames missed
+// by the birthPersistence requirement in the forward pass.
+function expandPartialsLeft(partials, frames) {
+ const trackingRatio = 0.05;
+ const minTrackingHz = 20;
+
+ // Build time → frame index map
+ const timeToIdx = new Map();
+ for (let i = 0; i < frames.length; ++i) timeToIdx.set(frames[i].time, i);
+
+ for (const partial of partials) {
+ let startIdx = timeToIdx.get(partial.times[0]);
+ if (startIdx == null || startIdx === 0) continue;
+
+ for (let i = startIdx - 1; i >= 0; --i) {
+ const frame = frames[i];
+ const refFreq = partial.freqs[0];
+ const tol = Math.max(refFreq * trackingRatio, minTrackingHz);
+
+ let bestIdx = -1, bestDist = Infinity;
+ for (let j = 0; j < frame.peaks.length; ++j) {
+ const dist = Math.abs(frame.peaks[j].freq - refFreq);
+ if (dist < tol && dist < bestDist) { bestDist = dist; bestIdx = j; }
+ }
+
+ if (bestIdx < 0) break;
+
+ const pk = frame.peaks[bestIdx];
+ partial.times.unshift(frame.time);
+ partial.freqs.unshift(pk.freq);
+ partial.amps.unshift(pk.amp);
+ }
+ }
+}
+
// Fit cubic bezier to trajectory using samples at ~1/3 and ~2/3 as control points
function fitBezier(times, values) {
const n = times.length - 1;