BoxOfColors commited on
Commit
41cd582
Β·
1 Parent(s): 5cf7a39

feat: add crossfade overlap indicators to waveform display

Browse files

Draws diagonal hatch pattern with 'CF' label over overlap zones between
adjacent segments so users can visually identify where crossfade blending
occurs. Helps correlate audio artifacts with crossfade regions during testing.

Files changed (1) hide show
  1. app.py +40 -3
app.py CHANGED
@@ -1066,7 +1066,8 @@ def _splice_and_save(new_wav, seg_idx, meta, slot_id):
1066
 
1067
  waveform_html = _build_waveform_html(audio_path, segments, slot_id, "",
1068
  state_json=state_json_new,
1069
- video_path=video_path)
 
1070
  return video_path, audio_path, updated_meta, waveform_html
1071
 
1072
 
@@ -1612,7 +1613,8 @@ def _build_regen_pending_html(segments: list, regen_seg_idx: int, slot_id: str,
1612
 
1613
  def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
1614
  hidden_input_id: str, state_json: str = "",
1615
- fn_index: int = -1, video_path: str = "") -> str:
 
1616
  """Return a self-contained HTML block with a Canvas waveform (display only),
1617
  segment boundary markers, and a download link.
1618
 
@@ -1659,6 +1661,7 @@ def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
1659
  const SLOT_ID = '{slot_id}';
1660
  const segments = {segs_json};
1661
  const segColors = {json.dumps(seg_colors)};
 
1662
  let audioDuration = 0;
1663
 
1664
  // ── Popup via postMessage to parent global listener ─────────────────
@@ -1746,6 +1749,39 @@ def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
1746
  }});
1747
  }});
1748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1749
  cv.onclick = function(e) {{
1750
  const r=cv.getBoundingClientRect();
1751
  const xRel=(e.clientX-r.left)/r.width;
@@ -1912,7 +1948,8 @@ def _unpack_outputs(flat: list, n: int, tab_prefix: str) -> list:
1912
  state_json = json.dumps(meta)
1913
  html = _build_waveform_html(aud_path, meta["segments"], slot_id,
1914
  "", state_json=state_json,
1915
- video_path=meta.get("video_path", ""))
 
1916
  wave_updates.append(gr.update(value=html))
1917
  else:
1918
  wave_updates.append(gr.update(
 
1066
 
1067
  waveform_html = _build_waveform_html(audio_path, segments, slot_id, "",
1068
  state_json=state_json_new,
1069
+ video_path=video_path,
1070
+ crossfade_s=crossfade_s)
1071
  return video_path, audio_path, updated_meta, waveform_html
1072
 
1073
 
 
1613
 
1614
  def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
1615
  hidden_input_id: str, state_json: str = "",
1616
+ fn_index: int = -1, video_path: str = "",
1617
+ crossfade_s: float = 0.0) -> str:
1618
  """Return a self-contained HTML block with a Canvas waveform (display only),
1619
  segment boundary markers, and a download link.
1620
 
 
1661
  const SLOT_ID = '{slot_id}';
1662
  const segments = {segs_json};
1663
  const segColors = {json.dumps(seg_colors)};
1664
+ const crossfadeSec = {crossfade_s};
1665
  let audioDuration = 0;
1666
 
1667
  // ── Popup via postMessage to parent global listener ─────────────────
 
1749
  }});
1750
  }});
1751
 
1752
+ // ── Crossfade overlap indicators ──
1753
+ if (crossfadeSec > 0 && segments.length > 1) {{
1754
+ for (let i = 0; i < segments.length - 1; i++) {{
1755
+ // Overlap zone: where segment i ends and segment i+1 starts
1756
+ const overlapStart = segments[i+1][0];
1757
+ const overlapEnd = Math.min(segments[i][1], overlapStart + crossfadeSec);
1758
+ const xL = (overlapStart / duration) * W;
1759
+ const xR = (overlapEnd / duration) * W;
1760
+ // Diagonal hatch pattern over the overlap zone
1761
+ ctx.save();
1762
+ ctx.beginPath();
1763
+ ctx.rect(xL, 0, xR - xL, H);
1764
+ ctx.clip();
1765
+ ctx.strokeStyle = 'rgba(255,255,255,0.35)';
1766
+ ctx.lineWidth = 1;
1767
+ const spacing = 6;
1768
+ for (let lx = xL - H; lx < xR + H; lx += spacing) {{
1769
+ ctx.beginPath();
1770
+ ctx.moveTo(lx, H);
1771
+ ctx.lineTo(lx + H, 0);
1772
+ ctx.stroke();
1773
+ }}
1774
+ ctx.restore();
1775
+ // Label
1776
+ ctx.fillStyle = 'rgba(255,255,255,0.5)';
1777
+ ctx.font = '9px sans-serif';
1778
+ const cfW = xR - xL;
1779
+ if (cfW > 20) {{
1780
+ ctx.fillText('CF', xL + (cfW - ctx.measureText('CF').width) / 2, H - 3);
1781
+ }}
1782
+ }}
1783
+ }}
1784
+
1785
  cv.onclick = function(e) {{
1786
  const r=cv.getBoundingClientRect();
1787
  const xRel=(e.clientX-r.left)/r.width;
 
1948
  state_json = json.dumps(meta)
1949
  html = _build_waveform_html(aud_path, meta["segments"], slot_id,
1950
  "", state_json=state_json,
1951
+ video_path=meta.get("video_path", ""),
1952
+ crossfade_s=float(meta.get("crossfade_s", 0)))
1953
  wave_updates.append(gr.update(value=html))
1954
  else:
1955
  wave_updates.append(gr.update(