Spaces:
Restarting
Restarting
Commit
·
1e3b782
1
Parent(s):
21a61ae
equal power crossfade in web app demo
Browse files- documentation.html +0 -5
- magentaRT_rt_tester.html +52 -15
documentation.html
CHANGED
@@ -407,11 +407,6 @@ files.download('/content/checkpoint_1862001.tgz')</pre>
|
|
407 |
<p><a href="/docs">auto-generated API docs (for all the http requests)</a></p>
|
408 |
</div>
|
409 |
|
410 |
-
<!-- <div class="section">
|
411 |
-
<h2>contributors</h2>
|
412 |
-
<p>Kevin Griffing and Andrew Luck</p>
|
413 |
-
</div> -->
|
414 |
-
|
415 |
<script>
|
416 |
function copyCode(button) {
|
417 |
const pre = button.parentElement;
|
|
|
407 |
<p><a href="/docs">auto-generated API docs (for all the http requests)</a></p>
|
408 |
</div>
|
409 |
|
|
|
|
|
|
|
|
|
|
|
410 |
<script>
|
411 |
function copyCode(button) {
|
412 |
const pre = button.parentElement;
|
magentaRT_rt_tester.html
CHANGED
@@ -106,8 +106,6 @@
|
|
106 |
<label class="small"><input id="chkAutoUpdate" type="checkbox" checked /> Auto-update on slider change (150ms debounce)</label>
|
107 |
<label class="small"><input id="chkLogAudio" type="checkbox" /> Log chunk sizes</label>
|
108 |
<label class="small"><input id="chkRealtime" type="checkbox" checked /> Ask server to pace real-time</label>
|
109 |
-
<!-- <label class="small"><input id="chkBootstrap" type="checkbox" checked /> Bootstrap fast (ASAP → flip to realtime)</label>
|
110 |
-
<label class="small">Pre-roll chunks: <input id="numPreroll" type="number" min="0" max="12" step="1" value="3" style="width:60px"></label> -->
|
111 |
</div>
|
112 |
</div>
|
113 |
</div>
|
@@ -241,8 +239,6 @@
|
|
241 |
const rngGuid = $("rngGuid"), numGuid = $("numGuid");
|
242 |
const rngTopk = $("rngTopk"), numTopk = $("numTopk");
|
243 |
const rngVol = $("rngVol"), numVol = $("numVol");
|
244 |
-
// const txtStyles = $("txtStyles");
|
245 |
-
// const txtStyleWeights = $("txtStyleWeights");
|
246 |
const chkUseMixStyle = $("chkUseMixStyle");
|
247 |
const statusEl = $("status");
|
248 |
const queueEl = $("queue");
|
@@ -268,8 +264,19 @@ const START_CUSHION = 0.12; // already used
|
|
268 |
|
269 |
const fade = XFADE_MS / 1000;
|
270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
function scheduleAudioBuffer(abuf) {
|
272 |
-
//
|
273 |
const src = ctx.createBufferSource();
|
274 |
const g = ctx.createGain();
|
275 |
src.buffer = abuf;
|
@@ -279,13 +286,47 @@ function scheduleAudioBuffer(abuf) {
|
|
279 |
const startAt = nextTime;
|
280 |
const dur = abuf.duration;
|
281 |
|
282 |
-
// Overlap by 'fade' so there
|
283 |
nextTime = startAt + Math.max(0, dur - fade);
|
284 |
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
|
290 |
src.start(startAt);
|
291 |
scheduled.push({ src, when: startAt, dur });
|
@@ -301,10 +342,6 @@ function beginPlaybackFromPending() {
|
|
301 |
const abuf = pending.shift();
|
302 |
scheduleAudioBuffer(abuf);
|
303 |
}
|
304 |
-
// Flip server pacing to realtime after we’ve started, if bootstrapping was enabled
|
305 |
-
// if ($("chkBootstrap").checked) {
|
306 |
-
// try { ws?.send(JSON.stringify({ type: "update", pace: "realtime" })); } catch {}
|
307 |
-
// }
|
308 |
}
|
309 |
|
310 |
// Audio chain
|
@@ -820,4 +857,4 @@ async function scheduleWavBytes(arrayBuffer) {
|
|
820 |
})();
|
821 |
</script>
|
822 |
</body>
|
823 |
-
</html>
|
|
|
106 |
<label class="small"><input id="chkAutoUpdate" type="checkbox" checked /> Auto-update on slider change (150ms debounce)</label>
|
107 |
<label class="small"><input id="chkLogAudio" type="checkbox" /> Log chunk sizes</label>
|
108 |
<label class="small"><input id="chkRealtime" type="checkbox" checked /> Ask server to pace real-time</label>
|
|
|
|
|
109 |
</div>
|
110 |
</div>
|
111 |
</div>
|
|
|
239 |
const rngGuid = $("rngGuid"), numGuid = $("numGuid");
|
240 |
const rngTopk = $("rngTopk"), numTopk = $("numTopk");
|
241 |
const rngVol = $("rngVol"), numVol = $("numVol");
|
|
|
|
|
242 |
const chkUseMixStyle = $("chkUseMixStyle");
|
243 |
const statusEl = $("status");
|
244 |
const queueEl = $("queue");
|
|
|
264 |
|
265 |
const fade = XFADE_MS / 1000;
|
266 |
|
267 |
+
// Equal-power crossfading functions
|
268 |
+
function equalPowerFadeOut(t) {
|
269 |
+
// cos²(t * π/2) where t goes from 0 to 1
|
270 |
+
return Math.cos(t * Math.PI / 2) ** 2;
|
271 |
+
}
|
272 |
+
|
273 |
+
function equalPowerFadeIn(t) {
|
274 |
+
// sin²(t * π/2) where t goes from 0 to 1
|
275 |
+
return Math.sin(t * Math.PI / 2) ** 2;
|
276 |
+
}
|
277 |
+
|
278 |
function scheduleAudioBuffer(abuf) {
|
279 |
+
// Equal-power crossfade scheduling
|
280 |
const src = ctx.createBufferSource();
|
281 |
const g = ctx.createGain();
|
282 |
src.buffer = abuf;
|
|
|
286 |
const startAt = nextTime;
|
287 |
const dur = abuf.duration;
|
288 |
|
289 |
+
// Overlap by 'fade' so there's no dip
|
290 |
nextTime = startAt + Math.max(0, dur - fade);
|
291 |
|
292 |
+
// Equal-power crossfading using custom curves
|
293 |
+
const numPoints = 64; // More points for smoother curves
|
294 |
+
const times = [];
|
295 |
+
const values = [];
|
296 |
+
|
297 |
+
// Fade in from 0 to 1 over fade duration
|
298 |
+
for (let i = 0; i <= numPoints; i++) {
|
299 |
+
const t = i / numPoints;
|
300 |
+
const time = startAt + t * fade;
|
301 |
+
const value = equalPowerFadeIn(t);
|
302 |
+
times.push(time);
|
303 |
+
values.push(value);
|
304 |
+
}
|
305 |
+
|
306 |
+
// Hold at 1.0 during the main portion
|
307 |
+
const holdStart = startAt + fade;
|
308 |
+
const holdEnd = startAt + Math.max(0, dur - fade);
|
309 |
+
if (holdEnd > holdStart) {
|
310 |
+
times.push(holdStart);
|
311 |
+
values.push(1.0);
|
312 |
+
times.push(holdEnd);
|
313 |
+
values.push(1.0);
|
314 |
+
}
|
315 |
+
|
316 |
+
// Fade out from 1 to 0 over fade duration
|
317 |
+
for (let i = 0; i <= numPoints; i++) {
|
318 |
+
const t = i / numPoints;
|
319 |
+
const time = startAt + Math.max(0, dur - fade) + t * fade;
|
320 |
+
const value = equalPowerFadeOut(t);
|
321 |
+
times.push(time);
|
322 |
+
values.push(value);
|
323 |
+
}
|
324 |
+
|
325 |
+
// Apply the envelope
|
326 |
+
g.gain.setValueAtTime(values[0], times[0]);
|
327 |
+
for (let i = 1; i < times.length; i++) {
|
328 |
+
g.gain.linearRampToValueAtTime(values[i], times[i]);
|
329 |
+
}
|
330 |
|
331 |
src.start(startAt);
|
332 |
scheduled.push({ src, when: startAt, dur });
|
|
|
342 |
const abuf = pending.shift();
|
343 |
scheduleAudioBuffer(abuf);
|
344 |
}
|
|
|
|
|
|
|
|
|
345 |
}
|
346 |
|
347 |
// Audio chain
|
|
|
857 |
})();
|
858 |
</script>
|
859 |
</body>
|
860 |
+
</html>
|