summaryrefslogtreecommitdiff
path: root/tools/mq_editor/PHASE_TRACKING_PLAN.md
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-18 14:00:18 +0100
committerskal <pascal.massimino@gmail.com>2026-02-18 14:00:18 +0100
commita9a60dfd2df938ef1e3ecc0a06d3d50cc329ef30 (patch)
treed4bfa5597eda60db9c0ea3ad20c53c39e228819a /tools/mq_editor/PHASE_TRACKING_PLAN.md
parentc3c1011cb6bf9bca28736b89049d76875a031ebe (diff)
feat(mq_editor): Implement phase-coherent partial trackingHEADmain
Implements a more robust partial tracking algorithm by using phase coherence to validate and link spectral peaks across frames. This significantly improves tracking accuracy, especially for crossing or closely-spaced partials. Key changes: - : The STFT cache now computes and stores the phase for each frequency bin alongside the magnitude. - : The peak detection logic now interpolates the phase for sub-bin accuracy. - : The core tracking algorithm was replaced with a phase-aware model. It predicts the expected phase for a partial in the next frame and uses a cost function combining both frequency and phase error to find the best match. This implementation follows the design outlined in the new document . handoff(Gemini): Phase prediction implemented. Further tuning of the cost function weights may be beneficial.
Diffstat (limited to 'tools/mq_editor/PHASE_TRACKING_PLAN.md')
-rw-r--r--tools/mq_editor/PHASE_TRACKING_PLAN.md73
1 files changed, 73 insertions, 0 deletions
diff --git a/tools/mq_editor/PHASE_TRACKING_PLAN.md b/tools/mq_editor/PHASE_TRACKING_PLAN.md
new file mode 100644
index 0000000..5111692
--- /dev/null
+++ b/tools/mq_editor/PHASE_TRACKING_PLAN.md
@@ -0,0 +1,73 @@
+# Implementation Plan: Phase-Coherent Partial Tracking
+
+This document outlines the plan to integrate phase prediction into the existing MQ tracking algorithm. The core idea is to use phase coherence as a primary factor for linking peaks across frames, making the tracking more robust, especially for crossing or closely-spaced partials.
+
+---
+
+### **Stage 1: Cache Per-Bin Phase in `fft.js`**
+
+**Objective:** Augment the `STFTCache` to compute and store the phase angle for every frequency bin in every frame, making it available for the tracking algorithm.
+
+1. **Locate the FFT Processing Loop:**
+ * In `tools/mq_editor/fft.js`, within the `STFTCache` class (likely in the constructor or an initialization method), find the loop that iterates through each frame to compute the FFT.
+ * This is where `squaredAmplitude` is currently being calculated from the `real` and `imag` components.
+
+2. **Compute and Store Phase:**
+ * In the same loop, immediately after calculating the squared amplitude, calculate the phase for each bin.
+ * Create a new `Float32Array` for phase, let's call it `ph`.
+ * Inside the bin loop, compute: `ph[k] = Math.atan2(imag[k], real[k]);`
+ * Store this new phase array in the frame object, parallel to the existing `squaredAmplitude` array.
+
+ **Resulting Change in `fft.js`:**
+
+ ```javascript
+ // Inside the STFTCache frame processing loop...
+
+ // Existing code:
+ const sq = new Float32Array(this.fftSize / 2);
+ for (let k = 0; k < this.fftSize / 2; k++) {
+ sq[k] = real[k] * real[k] + imag[k] * imag[k];
+ }
+
+ // New code to add:
+ const ph = new Float32Array(this.fftSize / 2);
+ for (let k = 0; k < this.fftSize / 2; k++) {
+ ph[k] = Math.atan2(imag[k], real[k]);
+ }
+
+ // Update the stored frame object
+ this.frames.push({
+ time: t,
+ squaredAmplitude: sq,
+ phase: ph // The newly cached phase data
+ });
+ ```
+
+---
+
+### **Stage 2: Utilize Phase for Tracking in `mq_extract.js`**
+
+**Objective:** Modify the main forward/backward tracking algorithm to use phase coherence for identifying and linking peaks.
+
+1. **Extract Interpolated Peak Phase:**
+ * In `tools/mq_editor/mq_extract.js`, find the function responsible for peak detection within a single frame (e.g., `findPeaks`).
+ * This function currently takes a `squaredAmplitude` array. It must now also access the corresponding `phase` array from the cached frame data.
+ * When a peak is found at bin `k`, use the same parabolic interpolation logic that calculates the true frequency and amplitude to also calculate the **true phase**. This involves interpolating the phase values from bins `k-1`, `k`, and `k+1`.
+ * **Crucially, this interpolation must handle phase wrapping.** A helper function will be needed to correctly find the shortest angular distance between phase values.
+
+2. **Update Tracking Data Structures:**
+ * The data structures holding candidate and live partials must be updated to store the phase of each point in the trajectory, not just frequency and amplitude.
+
+3. **Implement Phase Prediction Logic:**
+ * In the main tracking loop that steps from frame `n` to `n+1`:
+ * For each active partial, calculate its `predictedPhase` for frame `n+1`.
+ * `phase_delta = (2 * Math.PI * last_freq * params.hopSize) / params.sampleRate;`
+ * `predictedPhase = last_phase + phase_delta;`
+
+4. **Refine the Candidate Matching Score:**
+ * Modify the logic that links a partial to peaks in the next frame.
+ * Instead of matching based on frequency proximity alone, calculate a `cost` based on both frequency and phase deviation:
+ * `freqError = Math.abs(peak.freq - partial.last_freq);`
+ * `phaseError = Math.abs(normalize_angle(peak.phase - predictedPhase));` // Difference on a circle
+ * `cost = (freq_weight * freqError) + (phase_weight * phaseError);`
+ * The peak with the lowest `cost` below a certain threshold is the correct continuation. The `phase_weight` should be high, as a low phase error is a strong indicator of a correct match.