Audio Wave: snap splits to group line/frame + 'split @ groups' button
Double-click now snaps the split to the nearest 721 group line when within ~9px (so a segment ends exactly on frame 721), else to the nearest frame (frame-exact splits). New 'split @ groups' button drops a split on every group boundary at once, so covering the 721 grid is one click.
This commit is contained in:
+18
-6
@@ -64,10 +64,10 @@ function setupWave(node) {
|
||||
const bar = document.createElement("div");
|
||||
bar.style.cssText = "display:flex;gap:6px;align-items:center;font-size:10px;color:#bbb;flex-wrap:wrap;";
|
||||
const mk = (t) => { const b = document.createElement("button"); b.textContent = t; b.style.cssText = "font-size:10px;padding:1px 6px;"; return b; };
|
||||
const playBtn = mk("▶ play"), upBtn = mk("upload"), clearBtn = mk("clear splits");
|
||||
const playBtn = mk("▶ play"), upBtn = mk("upload"), grpBtn = mk("split @ groups"), clearBtn = mk("clear splits");
|
||||
const readout = document.createElement("span"); readout.textContent = "0.00 / 0.00s";
|
||||
const hint = document.createElement("span"); hint.textContent = "click=seek · dblclick=add split · shift-click a split=remove";
|
||||
bar.append(playBtn, upBtn, clearBtn, readout, hint);
|
||||
const hint = document.createElement("span"); hint.textContent = "click=seek · dblclick=add split (snaps to group line/frame) · shift-click a split=remove";
|
||||
bar.append(playBtn, upBtn, grpBtn, clearBtn, readout, hint);
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 900; canvas.height = 170;
|
||||
canvas.style.cssText = "width:100%;height:170px;background:#141418;border-radius:4px;cursor:pointer;";
|
||||
@@ -181,10 +181,16 @@ function setupWave(node) {
|
||||
}
|
||||
st.audio.currentTime = Math.max(0, Math.min(st.duration, t)); draw(); // click = seek
|
||||
});
|
||||
canvas.addEventListener("dblclick", (e) => { // add a user split
|
||||
function snap(t) { // snap to a group line if near, else to a frame
|
||||
const gs = groupFrames() / fps();
|
||||
const gt = Math.round(t / gs) * gs;
|
||||
if (gt > 0.02 && gt < st.duration - 0.02 && Math.abs(t2x(gt) - t2x(t)) < 9) return gt;
|
||||
return Math.round(t * fps()) / fps();
|
||||
}
|
||||
canvas.addEventListener("dblclick", (e) => { // add a user split (snapped)
|
||||
const r = canvas.getBoundingClientRect();
|
||||
const t = x2t(((e.clientX - r.left) / r.width) * canvas.width);
|
||||
if (t > 0.05 && t < st.duration - 0.05) { st.splits.push(t); serialize(); syncNotesBox(); draw(); }
|
||||
const t = snap(x2t(((e.clientX - r.left) / r.width) * canvas.width));
|
||||
if (t > 0.02 && t < st.duration - 0.02) { st.splits.push(t); serialize(); syncNotesBox(); draw(); }
|
||||
});
|
||||
|
||||
const loop = () => { if (!st.playing) return; draw(); requestAnimationFrame(loop); };
|
||||
@@ -194,6 +200,12 @@ function setupWave(node) {
|
||||
};
|
||||
st.audio.onended = () => { st.playing = false; playBtn.textContent = "▶ play"; draw(); };
|
||||
clearBtn.onclick = () => { st.splits = []; serialize(); syncNotesBox(); draw(); }; // wipe stale user splits
|
||||
grpBtn.onclick = () => { // one split on every 721 group line
|
||||
const set = new Set(st.splits.map((s) => Math.round(s * 100) / 100));
|
||||
for (const g of groupBounds()) set.add(Math.round(g * 100) / 100);
|
||||
st.splits = Array.from(set).sort((a, b) => a - b);
|
||||
serialize(); syncNotesBox(); draw();
|
||||
};
|
||||
|
||||
upBtn.onclick = () => {
|
||||
const inp = document.createElement("input"); inp.type = "file"; inp.accept = "audio/*";
|
||||
|
||||
Reference in New Issue
Block a user