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>
|