diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-18 15:46:59 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-18 15:46:59 +0100 |
| commit | 7a054e8ee8566eea9d06ff1ff9c1ce48c39fe659 (patch) | |
| tree | 30ee1013f398cc565139d67c2613200dbbebca6f /tools/mq_editor/index.html | |
| parent | 02dd7799396ebe3b5c3764796cfa3cbc72b72110 (diff) | |
feat(mq_editor): expose tracking params in UI with grouped param panel
Reorganize extraction parameters into four labeled groups (STFT / Peak
Detection / Tracking / Filter). Expose Birth, Death, Phase Wt and Min
Len as live controls wired to runExtraction(). All labels carry tooltip
descriptions. mq_extract.js now reads these from params instead of
hardcoded constants (same defaults preserved).
handoff(Claude): tracking params exposed, panel grouped, README updated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'tools/mq_editor/index.html')
| -rw-r--r-- | tools/mq_editor/index.html | 83 |
1 files changed, 67 insertions, 16 deletions
diff --git a/tools/mq_editor/index.html b/tools/mq_editor/index.html index a2daff5..5f6af24 100644 --- a/tools/mq_editor/index.html +++ b/tools/mq_editor/index.html @@ -43,11 +43,31 @@ #extractBtn { background: #666; color: #fff; font-weight: bold; border-color: #888; } input[type="file"] { display: none; } .params { - display: inline-block; - margin-left: 20px; + display: flex; + margin-top: 8px; + background: #222; + border: 1px solid #444; + border-radius: 3px; + } + .param-group { + display: flex; + align-items: center; + gap: 6px; + padding: 5px 12px; + border-right: 1px solid #444; + flex-wrap: wrap; + } + .param-group:last-child { border-right: none; } + .group-label { + font-size: 9px; + color: #666; + text-transform: uppercase; + letter-spacing: 1px; + white-space: nowrap; + margin-right: 2px; } label { - margin-right: 8px; + margin-right: 4px; } input[type="number"], select { width: 80px; @@ -266,22 +286,41 @@ <button id="stopBtn" disabled>■ Stop</button> <div class="params"> - <label>Hop:</label> - <input type="number" id="hopSize" value="256" min="64" max="1024" step="64"> - - <label>Threshold (dB):</label> - <input type="number" id="threshold" value="-20" step="any"> + <div class="param-group"> + <span class="group-label">STFT</span> + <label title="STFT hop size in samples. Smaller = finer time resolution, more frames, slower.">Hop</label> + <input type="number" id="hopSize" value="256" min="64" max="1024" step="64" style="width:60px;"> + </div> - <label>Prominence (dB):</label> - <input type="number" id="prominence" value="1.0" step="0.1" min="0"> + <div class="param-group"> + <span class="group-label">Peak Detection</span> + <label title="Minimum spectral peak amplitude in dB. Peaks below this are ignored.">Threshold (dB)</label> + <input type="number" id="threshold" value="-20" step="any"> + <label title="Minimum prominence in dB: how much a peak must stand above its surrounding valley. Suppresses weak shoulders.">Prominence (dB)</label> + <input type="number" id="prominence" value="1.0" step="0.1" min="0"> + <label title="Weight spectrum by frequency before peak detection: f × Power(f). Boosts high-frequency peaks relative to low-frequency ones."> + <input type="checkbox" id="freqWeight"> f·Power + </label> + </div> - <label style="margin-left:16px;" title="Weight spectrum by frequency before peak detection (f * FFT_Power(f)), accentuates high-frequency peaks"> - <input type="checkbox" id="freqWeight"> f·Power - </label> + <div class="param-group"> + <span class="group-label">Tracking</span> + <label title="Frames a candidate must persist consecutively before being promoted to a tracked partial. Higher = fewer spurious short bursts.">Birth</label> + <input type="number" id="birthPersistence" value="3" min="1" max="10" step="1" style="width:50px;"> + <label title="Frames a partial can go unmatched before it is terminated. Higher = bridges short gaps; lower = cuts off quickly.">Death</label> + <input type="number" id="deathAge" value="5" min="1" max="20" step="1" style="width:50px;"> + <label title="Weight of phase prediction error in the peak-matching cost function. Higher = stricter phase coherence required to continue a partial.">Phase Wt</label> + <input type="number" id="phaseErrorWeight" value="2.0" min="0" max="10" step="0.5" style="width:55px;"> + <label title="Minimum number of frames a tracked partial must span. Shorter partials are discarded after tracking.">Min Len</label> + <input type="number" id="minLength" value="10" min="1" max="50" step="1" style="width:50px;"> + </div> - <label style="margin-left:16px;">Keep:</label> - <input type="range" id="keepPct" min="1" max="100" value="100" style="width:100px; vertical-align:middle;"> - <span id="keepPctLabel" style="margin-left:4px;">100%</span> + <div class="param-group"> + <span class="group-label">Filter</span> + <label title="Keep only the strongest N% of extracted partials, ranked by peak amplitude.">Keep</label> + <input type="range" id="keepPct" min="1" max="100" value="100" style="width:100px; vertical-align:middle;"> + <span id="keepPctLabel">100%</span> + </div> </div> </div> @@ -451,6 +490,10 @@ const threshold = document.getElementById('threshold'); const prominence = document.getElementById('prominence'); const freqWeightCb = document.getElementById('freqWeight'); + const birthPersistenceEl = document.getElementById('birthPersistence'); + const deathAgeEl = document.getElementById('deathAge'); + const phaseErrorWeightEl = document.getElementById('phaseErrorWeight'); + const minLengthEl = document.getElementById('minLength'); const keepPct = document.getElementById('keepPct'); const keepPctLabel = document.getElementById('keepPctLabel'); const fftSize = 1024; // Fixed @@ -570,6 +613,10 @@ threshold: parseFloat(threshold.value), prominence: parseFloat(prominence.value), freqWeight: freqWeightCb.checked, + birthPersistence: parseInt(birthPersistenceEl.value), + deathAge: parseInt(deathAgeEl.value), + phaseErrorWeight: parseFloat(phaseErrorWeightEl.value), + minLength: parseInt(minLengthEl.value), sampleRate: audioBuffer.sampleRate }; @@ -629,6 +676,10 @@ if (stftCache) runExtraction(); }); + for (const el of [birthPersistenceEl, deathAgeEl, phaseErrorWeightEl, minLengthEl]) { + el.addEventListener('change', () => { if (stftCache) runExtraction(); }); + } + function playAudioBuffer(buffer, statusMsg) { const startTime = audioContext.currentTime; currentSource = audioContext.createBufferSource(); |
