BoxOfColors commited on
Commit
52e1535
·
1 Parent(s): 4d5093e

Fix waveform crossfade hatch + click hit-detection to use contact edges

Browse files

- Crossfade hatch was centered on segments[i+1][0] (old algo edge);
now centred on contactEdges[i] = (seg[i][1]+seg[i+1][0])/2,
which is where _stitch_wavs actually applies the blend.
- Click hit-detection used segments[idx+1][0] as boundary; now uses
contactEdges[idx] to match the visual coloring exactly.

Files changed (1) hide show
  1. app.py +9 -8
app.py CHANGED
@@ -2097,12 +2097,12 @@ def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
2097
  }});
2098
 
2099
  // ── Crossfade overlap indicators ──
2100
- // The color boundary is at segments[i+1][0] (= seg_i.end - crossfadeSec).
2101
- // We centre the hatch on that edge: half the crossfade on each color side.
2102
  if (crossfadeSec > 0 && segments.length > 1) {{
2103
  for (let i = 0; i < segments.length - 1; i++) {{
2104
- // Color edge = segments[i+1][0], hatch spans half on each side
2105
- const edgeT = segments[i+1][0];
2106
  const overlapStart = edgeT - crossfadeSec / 2;
2107
  const overlapEnd = edgeT + crossfadeSec / 2;
2108
  const xL = (overlapStart / duration) * W;
@@ -2129,12 +2129,13 @@ def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
2129
  const r=cv.getBoundingClientRect();
2130
  const xRel=(e.clientX-r.left)/r.width;
2131
  const tClick=xRel*duration;
2132
- // Pick the segment whose unique (non-overlapping) region contains the click.
2133
- // Each segment owns [seg[0], nextSeg[0]) visually; last segment owns [seg[0], seg[1]].
2134
  let hit=-1;
2135
  segments.forEach(function(seg,idx){{
2136
- const uniqueEnd = idx + 1 < segments.length ? segments[idx+1][0] : seg[1];
2137
- if (tClick >= seg[0] && tClick < uniqueEnd) hit = idx;
 
2138
  }});
2139
  console.log('[wf click] tClick='+tClick.toFixed(2)+' hit='+hit+' audioDuration='+audioDuration+' segments='+JSON.stringify(segments));
2140
  if (hit>=0) showPopup(hit, e.clientX, e.clientY);
 
2097
  }});
2098
 
2099
  // ── Crossfade overlap indicators ──
2100
+ // The crossfade is applied ±crossfadeSec/2 around each contact edge.
2101
+ // contactEdges[i] = midpoint of overlap between seg i and seg i+1.
2102
  if (crossfadeSec > 0 && segments.length > 1) {{
2103
  for (let i = 0; i < segments.length - 1; i++) {{
2104
+ // Hatch spans crossfadeSec centred on the contact edge
2105
+ const edgeT = contactEdges[i];
2106
  const overlapStart = edgeT - crossfadeSec / 2;
2107
  const overlapEnd = edgeT + crossfadeSec / 2;
2108
  const xL = (overlapStart / duration) * W;
 
2129
  const r=cv.getBoundingClientRect();
2130
  const xRel=(e.clientX-r.left)/r.width;
2131
  const tClick=xRel*duration;
2132
+ // Pick the segment by contact-edge boundaries matches the visual coloring.
2133
+ // Seg 0 owns [0, contactEdges[0]), seg N-1 owns [contactEdges[N-2], duration).
2134
  let hit=-1;
2135
  segments.forEach(function(seg,idx){{
2136
+ const visStart = idx === 0 ? 0 : contactEdges[idx-1];
2137
+ const visEnd = idx === segments.length - 1 ? duration : contactEdges[idx];
2138
+ if (tClick >= visStart && tClick < visEnd) hit = idx;
2139
  }});
2140
  console.log('[wf click] tClick='+tClick.toFixed(2)+' hit='+hit+' audioDuration='+audioDuration+' segments='+JSON.stringify(segments));
2141
  if (hit>=0) showPopup(hit, e.clientX, e.clientY);