From fc9cfd50a584faa2f6621ea1b4ff4eeb6b50f8e4 Mon Sep 17 00:00:00 2001 From: skal Date: Wed, 18 Feb 2026 21:07:40 +0100 Subject: fix(mq_editor): key '3' plays partial from t_start; add getAudioBuffer() - synthesizeMQ output trimmed to [t_start-50ms, t_end+50ms] so playback starts immediately at the partial instead of t=0 - Extract synth+trim logic into getAudioBuffer(partials, margin=0) - Stack params vertically in dropdown (grid layout) handoff(Claude): partial playback and CSS param layout fixes Co-Authored-By: Claude Sonnet 4.6 --- tools/mq_editor/app.js | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'tools/mq_editor/app.js') diff --git a/tools/mq_editor/app.js b/tools/mq_editor/app.js index 20224a0..9857c64 100644 --- a/tools/mq_editor/app.js +++ b/tools/mq_editor/app.js @@ -481,6 +481,25 @@ function getSynthParams() { }; } +// Synthesize partials and return an AudioBuffer, trimmed to [t_start-margin, t_end+margin] +function getAudioBuffer(partials, margin = 0) { + const sr = audioBuffer.sampleRate; + const {integratePhase, opts} = getSynthParams(); + const pcm = synthesizeMQ(partials, sr, audioBuffer.duration, integratePhase, opts); + let startSample = 0, endSample = pcm.length; + if (margin >= 0 && partials.length > 0) { + const times = partials.flatMap(p => p.times); + const tStart = Math.min(...times); + const tEnd = Math.max(...times); + startSample = Math.max(0, Math.floor((tStart - margin) * sr)); + endSample = Math.min(pcm.length, Math.ceil((tEnd + margin) * sr)); + } + const trimmed = pcm.subarray(startSample, endSample); + const buf = audioContext.createBuffer(1, trimmed.length, sr); + buf.getChannelData(0).set(trimmed); + return buf; +} + // Play synthesized audio function playSynthesized() { if (!extractedPartials || extractedPartials.length === 0) { @@ -497,16 +516,14 @@ function playSynthesized() { const partialsToUse = extractedPartials.slice(0, keepCount).filter(p => !p.muted); setStatus(`Synthesizing ${partialsToUse.length}/${extractedPartials.length} partials (${keepPct.value}%)...`, 'info'); - const {integratePhase, opts} = getSynthParams(); - const pcm = synthesizeMQ(partialsToUse, audioBuffer.sampleRate, audioBuffer.duration, - integratePhase, opts); - if (viewer) { + const {integratePhase, opts} = getSynthParams(); + const pcm = synthesizeMQ(partialsToUse, audioBuffer.sampleRate, audioBuffer.duration, + integratePhase, opts); viewer.setSynthStftCache(new STFTCache(pcm, audioBuffer.sampleRate, fftSize, parseInt(hopSize.value))); } - const synthBuffer = audioContext.createBuffer(1, pcm.length, audioBuffer.sampleRate); - synthBuffer.getChannelData(0).set(pcm); + const synthBuffer = getAudioBuffer(partialsToUse); playAudioBuffer(synthBuffer, `Playing synthesized (${partialsToUse.length}/${extractedPartials.length} partials, ${keepPct.value}%)...`); } @@ -535,12 +552,7 @@ document.addEventListener('keydown', (e) => { const partial = extractedPartials[sel]; if (!partial) return; stopAudio(); - const {integratePhase, opts} = getSynthParams(); - const pcm = synthesizeMQ([partial], audioBuffer.sampleRate, audioBuffer.duration, - integratePhase, opts); - const buf = audioContext.createBuffer(1, pcm.length, audioBuffer.sampleRate); - buf.getChannelData(0).set(pcm); - playAudioBuffer(buf, `Playing partial #${sel}...`); + playAudioBuffer(getAudioBuffer([partial], 0.05), `Playing partial #${sel}...`); } else if (e.code === 'KeyP') { e.preventDefault(); if (viewer) viewer.togglePeaks(); -- cgit v1.2.3