Spaces:
Running on Zero
Running on Zero
Commit Β·
1cf1eb7
1
Parent(s): 504a99f
app.py
CHANGED
|
@@ -1209,7 +1209,9 @@ def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
|
|
| 1209 |
const canvas = document.getElementById('wf_canvas_{slot_id}');
|
| 1210 |
if (!canvas) return;
|
| 1211 |
const dpr = window.devicePixelRatio || 1;
|
| 1212 |
-
const W = canvas.
|
|
|
|
|
|
|
| 1213 |
const H = 80;
|
| 1214 |
canvas.width = W * dpr;
|
| 1215 |
canvas.height = H * dpr;
|
|
@@ -1276,7 +1278,9 @@ def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
|
|
| 1276 |
const canvas = document.getElementById('wf_playhead_{slot_id}');
|
| 1277 |
if (!canvas) return;
|
| 1278 |
const dpr = window.devicePixelRatio || 1;
|
| 1279 |
-
const W = canvas.
|
|
|
|
|
|
|
| 1280 |
const H = 80;
|
| 1281 |
if (canvas.width !== W*dpr) {{ canvas.width=W*dpr; canvas.height=H*dpr; }}
|
| 1282 |
const ctx = canvas.getContext('2d');
|
|
@@ -1317,33 +1321,39 @@ def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
|
|
| 1317 |
}}
|
| 1318 |
|
| 1319 |
// ββ Decode audio & render βββββββββββββββββββββββββββββββββββββββββ
|
| 1320 |
-
|
| 1321 |
-
|
| 1322 |
-
|
| 1323 |
-
|
| 1324 |
-
|
| 1325 |
-
|
| 1326 |
-
|
| 1327 |
-
|
| 1328 |
-
|
| 1329 |
-
|
| 1330 |
-
|
|
|
|
|
|
|
| 1331 |
const channelData = audioBuffer.getChannelData(0);
|
| 1332 |
-
|
| 1333 |
-
|
| 1334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1335 |
}}, function(err) {{
|
| 1336 |
console.error('[waveform {slot_id}] decodeAudioData error:', err);
|
| 1337 |
}});
|
| 1338 |
}}
|
| 1339 |
-
|
| 1340 |
-
// Run after the canvas is in the DOM and has layout
|
| 1341 |
-
if (document.readyState === 'loading') {{
|
| 1342 |
-
document.addEventListener('DOMContentLoaded', init);
|
| 1343 |
-
}} else {{
|
| 1344 |
-
// Small delay to let Gradio finish laying out the component
|
| 1345 |
-
setTimeout(init, 100);
|
| 1346 |
-
}}
|
| 1347 |
}})();
|
| 1348 |
</script>
|
| 1349 |
"""
|
|
@@ -1359,7 +1369,7 @@ def _make_output_slots(tab_prefix: str) -> tuple:
|
|
| 1359 |
for i in range(MAX_SLOTS):
|
| 1360 |
with gr.Group(visible=(i == 0)) as g:
|
| 1361 |
slot_id = f"{tab_prefix}_{i}"
|
| 1362 |
-
vids.append(gr.Video(label=f"Generation {i+1} β Video"
|
| 1363 |
waveforms.append(gr.HTML(
|
| 1364 |
value="<p style='color:#888;font-size:12px'>Generate audio to see waveform.</p>",
|
| 1365 |
label=f"Generation {i+1} β Waveform",
|
|
@@ -1426,7 +1436,23 @@ def _update_slot_visibility(n):
|
|
| 1426 |
# GRADIO UI #
|
| 1427 |
# ================================================================== #
|
| 1428 |
|
| 1429 |
-
_SLOT_CSS = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1430 |
|
| 1431 |
with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS) as demo:
|
| 1432 |
gr.Markdown(
|
|
|
|
| 1209 |
const canvas = document.getElementById('wf_canvas_{slot_id}');
|
| 1210 |
if (!canvas) return;
|
| 1211 |
const dpr = window.devicePixelRatio || 1;
|
| 1212 |
+
const W = canvas.getBoundingClientRect().width
|
| 1213 |
+
|| (canvas.parentElement ? canvas.parentElement.getBoundingClientRect().width : 0)
|
| 1214 |
+
|| 600;
|
| 1215 |
const H = 80;
|
| 1216 |
canvas.width = W * dpr;
|
| 1217 |
canvas.height = H * dpr;
|
|
|
|
| 1278 |
const canvas = document.getElementById('wf_playhead_{slot_id}');
|
| 1279 |
if (!canvas) return;
|
| 1280 |
const dpr = window.devicePixelRatio || 1;
|
| 1281 |
+
const W = canvas.getBoundingClientRect().width
|
| 1282 |
+
|| (canvas.parentElement ? canvas.parentElement.getBoundingClientRect().width : 0)
|
| 1283 |
+
|| 600;
|
| 1284 |
const H = 80;
|
| 1285 |
if (canvas.width !== W*dpr) {{ canvas.width=W*dpr; canvas.height=H*dpr; }}
|
| 1286 |
const ctx = canvas.getContext('2d');
|
|
|
|
| 1321 |
}}
|
| 1322 |
|
| 1323 |
// ββ Decode audio & render βββββββββββββββββββββββββββββββββββββββββ
|
| 1324 |
+
// Decode audio immediately (doesn't need canvas dimensions).
|
| 1325 |
+
// Drawing is deferred until the canvas actually has a non-zero width.
|
| 1326 |
+
const b64str = '{b64}';
|
| 1327 |
+
const bin = atob(b64str);
|
| 1328 |
+
const buf = new Uint8Array(bin.length);
|
| 1329 |
+
for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
|
| 1330 |
+
|
| 1331 |
+
const AudioCtx = window.AudioContext || window.webkitAudioContext;
|
| 1332 |
+
if (!AudioCtx) {{ console.warn('[waveform {slot_id}] No AudioContext available'); }}
|
| 1333 |
+
else {{
|
| 1334 |
+
// Use OfflineAudioContext so decoding works even without user gesture
|
| 1335 |
+
const tmpCtx = new AudioCtx({{latencyHint:'playback', sampleRate:44100}});
|
| 1336 |
+
tmpCtx.decodeAudioData(buf.buffer.slice(0), function(audioBuffer) {{
|
| 1337 |
const channelData = audioBuffer.getChannelData(0);
|
| 1338 |
+
const duration = audioBuffer.duration;
|
| 1339 |
+
tmpCtx.close();
|
| 1340 |
+
|
| 1341 |
+
// Now wait for the canvas to have layout width before drawing
|
| 1342 |
+
function tryDraw() {{
|
| 1343 |
+
const canvas = document.getElementById('wf_canvas_{slot_id}');
|
| 1344 |
+
if (!canvas) {{ setTimeout(tryDraw, 100); return; }}
|
| 1345 |
+
// Force a width by walking up to a sized ancestor if needed
|
| 1346 |
+
let W = canvas.getBoundingClientRect().width;
|
| 1347 |
+
if (W <= 0) W = canvas.parentElement ? canvas.parentElement.getBoundingClientRect().width : 0;
|
| 1348 |
+
if (W <= 0) {{ setTimeout(tryDraw, 100); return; }}
|
| 1349 |
+
drawWaveform(channelData, duration);
|
| 1350 |
+
attachVideoSync();
|
| 1351 |
+
}}
|
| 1352 |
+
tryDraw();
|
| 1353 |
}}, function(err) {{
|
| 1354 |
console.error('[waveform {slot_id}] decodeAudioData error:', err);
|
| 1355 |
}});
|
| 1356 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1357 |
}})();
|
| 1358 |
</script>
|
| 1359 |
"""
|
|
|
|
| 1369 |
for i in range(MAX_SLOTS):
|
| 1370 |
with gr.Group(visible=(i == 0)) as g:
|
| 1371 |
slot_id = f"{tab_prefix}_{i}"
|
| 1372 |
+
vids.append(gr.Video(label=f"Generation {i+1} β Video"))
|
| 1373 |
waveforms.append(gr.HTML(
|
| 1374 |
value="<p style='color:#888;font-size:12px'>Generate audio to see waveform.</p>",
|
| 1375 |
label=f"Generation {i+1} β Waveform",
|
|
|
|
| 1436 |
# GRADIO UI #
|
| 1437 |
# ================================================================== #
|
| 1438 |
|
| 1439 |
+
_SLOT_CSS = """
|
| 1440 |
+
/* Make all video components (input and output) use the same responsive
|
| 1441 |
+
aspect-ratio container so they match at any window size. */
|
| 1442 |
+
.gradio-video,
|
| 1443 |
+
.gradio-video > div {
|
| 1444 |
+
aspect-ratio: 16 / 9;
|
| 1445 |
+
width: 100%;
|
| 1446 |
+
height: auto !important;
|
| 1447 |
+
min-height: unset !important;
|
| 1448 |
+
max-height: unset !important;
|
| 1449 |
+
}
|
| 1450 |
+
.gradio-video video {
|
| 1451 |
+
width: 100%;
|
| 1452 |
+
height: 100%;
|
| 1453 |
+
object-fit: contain;
|
| 1454 |
+
}
|
| 1455 |
+
"""
|
| 1456 |
|
| 1457 |
with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS) as demo:
|
| 1458 |
gr.Markdown(
|