Audio Wave: segments = user splits only (721 groups are markers, not segments)
Per clarification: the 721 group lines are clip markers, NOT segments. Segments come ONLY from user split points, numbered globally from 1 — so the first split is seg1, not seg23. 0 splits = 0 segments; N splits = N segments (seg k = point k-1 .. split k, tail after the last split excluded). group lines still drawn (bold) + used for the group#. JS matches (segPoints/nSegs). Empty summary now prompts to add splits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -180,6 +180,9 @@ def _render(rms_n, times, duration, beats, bounds, segs,
|
|||||||
|
|
||||||
|
|
||||||
def _summary(duration, sr, bpm, beats, segs, global_notes):
|
def _summary(duration, sr, bpm, beats, segs, global_notes):
|
||||||
|
if not segs:
|
||||||
|
return (f"AUDIO GUIDE — {duration:.2f}s clip. No segments defined yet: double-click "
|
||||||
|
f"the waveform to add split points (each split starts a new beat).")
|
||||||
lines = ["AUDIO GUIDE — align the video beats to this audio:",
|
lines = ["AUDIO GUIDE — align the video beats to this audio:",
|
||||||
f"- duration: {duration:.2f}s | sample_rate: {sr} Hz"]
|
f"- duration: {duration:.2f}s | sample_rate: {sr} Hz"]
|
||||||
if bpm:
|
if bpm:
|
||||||
|
|||||||
@@ -108,19 +108,19 @@ def _parse_range(sel, n):
|
|||||||
|
|
||||||
|
|
||||||
def _segments_grouped(rms_n, times, duration, fps, group_frames, user_starts, beats):
|
def _segments_grouped(rms_n, times, duration, fps, group_frames, user_starts, beats):
|
||||||
"""Segments = user split points UNION the fixed group boundaries (every group_frames
|
"""Segments come ONLY from the user's split points (the 721 group lines are just clip
|
||||||
frames). The group lines are hard splits, so a segment never crosses one — the last
|
markers, NOT segments). Points = [0] + splits; a segment runs between consecutive
|
||||||
segment in a group ends exactly on the group boundary (e.g. frame 721)."""
|
points, so N splits -> N segments (the tail after the last split is not a segment).
|
||||||
|
Global numbering. `group` = which 721 group each segment starts in (for reference)."""
|
||||||
gstep = max(group_frames, 1) / max(fps, 1)
|
gstep = max(group_frames, 1) / max(fps, 1)
|
||||||
gbounds, k = [], 1
|
gbounds, k = [], 1
|
||||||
while k * gstep < duration - 1e-6:
|
while k * gstep < duration - 1e-6: # for drawing + the group number only
|
||||||
gbounds.append(round(k * gstep, 3)); k += 1
|
gbounds.append(round(k * gstep, 3)); k += 1
|
||||||
allb = sorted({round(b, 3) for b in list(user_starts) + gbounds if 0 < b < duration - 1e-6})
|
pts = [0.0] + sorted({round(s, 3) for s in user_starts if 0 < s < duration - 1e-6})
|
||||||
starts = [0.0] + allb
|
|
||||||
stages = ["establish", "build", "peak", "settle"]
|
stages = ["establish", "build", "peak", "settle"]
|
||||||
segs, bounds = [], [0.0]
|
segs, bounds = [], [0.0]
|
||||||
for i, t0 in enumerate(starts):
|
for i in range(len(pts) - 1): # exclude the tail (last point -> end)
|
||||||
t1 = starts[i + 1] if i + 1 < len(starts) else duration
|
t0, t1 = pts[i], pts[i + 1]
|
||||||
bounds.append(round(t1, 3))
|
bounds.append(round(t1, 3))
|
||||||
mask = (times >= t0) & (times < t1)
|
mask = (times >= t0) & (times < t1)
|
||||||
e = float(rms_n[mask].mean()) if mask.any() else 0.0
|
e = float(rms_n[mask].mean()) if mask.any() else 0.0
|
||||||
|
|||||||
+12
-9
@@ -91,12 +91,12 @@ function setupWave(node) {
|
|||||||
for (let t = step; t < st.duration - 1e-6; t += step) arr.push(t);
|
for (let t = step; t < st.duration - 1e-6; t += step) arr.push(t);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
function segStarts() { // union of 0 + user splits + group lines
|
function segPoints() { // [0] + user splits (NOT the group lines)
|
||||||
const set = new Set([0]);
|
const set = new Set([0]);
|
||||||
for (const s of st.splits) if (s > 0 && s < st.duration) set.add(Math.round(s * 100) / 100);
|
for (const s of st.splits) if (s > 0 && s < st.duration) set.add(Math.round(s * 100) / 100);
|
||||||
for (const g of groupBounds()) set.add(Math.round(g * 100) / 100);
|
|
||||||
return Array.from(set).sort((a, b) => a - b);
|
return Array.from(set).sort((a, b) => a - b);
|
||||||
}
|
}
|
||||||
|
const nSegs = () => Math.max(0, segPoints().length - 1); // N splits -> N segments (tail excluded)
|
||||||
|
|
||||||
function serialize() { // write ONLY user splits (small + stable)
|
function serialize() { // write ONLY user splits (small + stable)
|
||||||
const w = getW(node, "segments_json"); if (!w) return;
|
const w = getW(node, "segments_json"); if (!w) return;
|
||||||
@@ -111,7 +111,7 @@ function setupWave(node) {
|
|||||||
function syncNotesBox() { // one segN: line per segment, preserve notes
|
function syncNotesBox() { // one segN: line per segment, preserve notes
|
||||||
const nw = getW(node, "notes"); if (!nw) return;
|
const nw = getW(node, "notes"); if (!nw) return;
|
||||||
const { seg, glob } = parseNotes(nw.value);
|
const { seg, glob } = parseNotes(nw.value);
|
||||||
const n = segStarts().length; const lines = [];
|
const n = nSegs(); const lines = [];
|
||||||
for (let i = 1; i <= n; i++) lines.push(`seg${i}: ${seg[i] !== undefined ? seg[i] : ""}`);
|
for (let i = 1; i <= n; i++) lines.push(`seg${i}: ${seg[i] !== undefined ? seg[i] : ""}`);
|
||||||
const val = lines.concat(glob).join("\n");
|
const val = lines.concat(glob).join("\n");
|
||||||
if (nw.value !== val) { nw.value = val; nw.callback?.(val); }
|
if (nw.value !== val) { nw.value = val; nw.callback?.(val); }
|
||||||
@@ -127,10 +127,10 @@ function setupWave(node) {
|
|||||||
function draw() {
|
function draw() {
|
||||||
ctx.fillStyle = "#141418"; ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillStyle = "#141418"; ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
const H = canvas.height, mid = H / 2;
|
const H = canvas.height, mid = H / 2;
|
||||||
const starts = segStarts();
|
const pts = segPoints(); // [0, split1, split2, ...]; seg k = pts[k-1]..pts[k]
|
||||||
const sel = selRange();
|
const sel = selRange();
|
||||||
if (sel) { // shade selected range
|
if (sel) { // shade selected segment range
|
||||||
const a = starts[sel[0] - 1], b = sel[1] < starts.length ? starts[sel[1]] : st.duration;
|
const a = pts[sel[0] - 1], b = sel[1] < pts.length ? pts[sel[1]] : st.duration;
|
||||||
if (a !== undefined) { ctx.fillStyle = "#26364f"; ctx.fillRect(t2x(a), 0, t2x(b) - t2x(a), H); }
|
if (a !== undefined) { ctx.fillStyle = "#26364f"; ctx.fillRect(t2x(a), 0, t2x(b) - t2x(a), H); }
|
||||||
}
|
}
|
||||||
if (st.peaks) { // mirrored waveform around centre
|
if (st.peaks) { // mirrored waveform around centre
|
||||||
@@ -144,11 +144,14 @@ function setupWave(node) {
|
|||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 1;
|
||||||
const { seg } = parseNotes(getW(node, "notes")?.value);
|
const { seg } = parseNotes(getW(node, "notes")?.value);
|
||||||
ctx.font = "10px monospace";
|
ctx.font = "10px monospace";
|
||||||
starts.forEach((s, i) => { // segment line + label top + note bottom
|
const nseg = nSegs();
|
||||||
|
pts.forEach((s, i) => { // split line; label seg i+1 (skip tail)
|
||||||
const x = t2x(s);
|
const x = t2x(s);
|
||||||
ctx.strokeStyle = "#7ec8a0"; ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke();
|
ctx.strokeStyle = "#7ec8a0"; ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke();
|
||||||
ctx.fillStyle = "#fff"; ctx.fillText(`S${i + 1} ${s.toFixed(1)}s`, x + 3, 11);
|
if (i < nseg) {
|
||||||
if (seg[i + 1]) { ctx.fillStyle = "#ffd27a"; ctx.fillText(seg[i + 1].slice(0, 22), x + 3, H - 4); }
|
ctx.fillStyle = "#fff"; ctx.fillText(`S${i + 1} ${s.toFixed(1)}s`, x + 3, 11);
|
||||||
|
if (seg[i + 1]) { ctx.fillStyle = "#ffd27a"; ctx.fillText(seg[i + 1].slice(0, 22), x + 3, H - 4); }
|
||||||
|
}
|
||||||
});
|
});
|
||||||
ctx.strokeStyle = "#ff5a3c"; const px = t2x(st.audio.currentTime || 0); // playhead
|
ctx.strokeStyle = "#ff5a3c"; const px = t2x(st.audio.currentTime || 0); // playhead
|
||||||
ctx.beginPath(); ctx.moveTo(px, 0); ctx.lineTo(px, H); ctx.stroke();
|
ctx.beginPath(); ctx.moveTo(px, 0); ctx.lineTo(px, H); ctx.stroke();
|
||||||
|
|||||||
Reference in New Issue
Block a user