@kieransimkin/dancemoves 3.1.23 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/docs/audio-recording-playback.md +56 -0
- package/lib/build-manifest.json +11 -11
- package/lib/dancemoves.min.js +3 -3
- package/lib/index.cjs +242 -63
- package/lib/index.d.ts +5 -1
- package/lib/index.mjs +242 -63
- package/lib/wordpress.js +3 -3
- package/package.json +1 -1
- package/readme.txt +2 -2
package/lib/index.cjs
CHANGED
|
@@ -1065,7 +1065,7 @@ function install2(__dmScope) {
|
|
|
1065
1065
|
return;
|
|
1066
1066
|
var instances = /* @__PURE__ */ new Map();
|
|
1067
1067
|
var serial2 = 0;
|
|
1068
|
-
function
|
|
1068
|
+
function finite3(value, fallback) {
|
|
1069
1069
|
value = Number(value);
|
|
1070
1070
|
return Number.isFinite(value) ? value : fallback;
|
|
1071
1071
|
}
|
|
@@ -1119,8 +1119,8 @@ function install2(__dmScope) {
|
|
|
1119
1119
|
rect = bounds.getBoundingClientRect();
|
|
1120
1120
|
}
|
|
1121
1121
|
function apply(x, y, reason) {
|
|
1122
|
-
latestX = clamp(
|
|
1123
|
-
latestY = clamp(
|
|
1122
|
+
latestX = clamp(finite3(x, 0), -1, 1);
|
|
1123
|
+
latestY = clamp(finite3(y, 0), -1, 1);
|
|
1124
1124
|
__dmScope.setStyle(state.root.style, "--dance-moves-x", latestX.toFixed(4));
|
|
1125
1125
|
__dmScope.setStyle(state.root.style, "--dance-moves-y", latestY.toFixed(4));
|
|
1126
1126
|
__dmScope.dataset(state.root, "danceMovesPointer", reason || "active");
|
|
@@ -1211,13 +1211,13 @@ function install2(__dmScope) {
|
|
|
1211
1211
|
var audio = playbackElement(options.audio, state.root.querySelector("audio"));
|
|
1212
1212
|
if (!audio)
|
|
1213
1213
|
throw new Error("DanceMovesEffects pulse requires an audio element");
|
|
1214
|
-
var ticks = Math.max(1, Math.round(
|
|
1214
|
+
var ticks = Math.max(1, Math.round(finite3(options.ticks, 16)));
|
|
1215
1215
|
var className = String(options.className || "dance-moves-playing");
|
|
1216
1216
|
var prefix = String(options.propertyPrefix || "--dance-moves-pulse").replace(/-+$/, "");
|
|
1217
1217
|
function sync() {
|
|
1218
|
-
var rate = Math.max(0.01,
|
|
1218
|
+
var rate = Math.max(0.01, finite3(audio.playbackRate, 1));
|
|
1219
1219
|
var duration = motion.durationMilliseconds(ticks) / 1e3 / rate;
|
|
1220
|
-
var phase = -(
|
|
1220
|
+
var phase = -(finite3(audio.currentTime, 0) / rate % duration);
|
|
1221
1221
|
__dmScope.setStyle(state.root.style, prefix + "-duration", duration.toFixed(6) + "s");
|
|
1222
1222
|
__dmScope.setStyle(state.root.style, prefix + "-delay", phase.toFixed(6) + "s");
|
|
1223
1223
|
__dmScope.dataset(state.root, "danceMovesPulse", state.reduced.matches || state.forced.matches ? "inactive" : audio.paused ? "paused" : "playing");
|
|
@@ -1256,7 +1256,7 @@ function install2(__dmScope) {
|
|
|
1256
1256
|
var state = commonState(options, "cue-class");
|
|
1257
1257
|
var cue = String(options.cue || "*");
|
|
1258
1258
|
var className = String(options.className || "dance-moves-cue-active");
|
|
1259
|
-
var durationTicks = Math.max(1, Math.round(
|
|
1259
|
+
var durationTicks = Math.max(1, Math.round(finite3(options.durationTicks, 16)));
|
|
1260
1260
|
var timer = 0, count = 0;
|
|
1261
1261
|
function clear(reason) {
|
|
1262
1262
|
if (timer)
|
|
@@ -1312,16 +1312,16 @@ function install2(__dmScope) {
|
|
|
1312
1312
|
if (!audio)
|
|
1313
1313
|
throw new Error("DanceMovesEffects cueTimeline requires an audio element");
|
|
1314
1314
|
var cues = (Array.isArray(options.cues) ? options.cues : []).map(function(cue, index) {
|
|
1315
|
-
var time =
|
|
1315
|
+
var time = finite3(cue && cue.time, NaN);
|
|
1316
1316
|
if (!Number.isFinite(time) || time < 0)
|
|
1317
1317
|
throw new TypeError("DanceMovesEffects cueTimeline cue time must be a non-negative number");
|
|
1318
1318
|
return Object.freeze({ id: String(cue.id || "cue-" + index), time, end: Number.isFinite(Number(cue.end)) ? Number(cue.end) : null, data: cue.data });
|
|
1319
1319
|
}).sort(function(a, b) {
|
|
1320
1320
|
return a.time - b.time;
|
|
1321
1321
|
});
|
|
1322
|
-
var tolerance = Math.max(0,
|
|
1323
|
-
var maxCrossingGap = Math.max(0.05,
|
|
1324
|
-
var fired = /* @__PURE__ */ new Set(), frame = 0, previous =
|
|
1322
|
+
var tolerance = Math.max(0, finite3(options.seekLandingTolerance, 0.05));
|
|
1323
|
+
var maxCrossingGap = Math.max(0.05, finite3(options.maxCrossingGap, 1.25));
|
|
1324
|
+
var fired = /* @__PURE__ */ new Set(), frame = 0, previous = finite3(audio.currentTime, 0), status = "idle", generation = 0, previousWall = 0;
|
|
1325
1325
|
function blocked() {
|
|
1326
1326
|
return document2.hidden || state.reduced.matches || state.forced.matches;
|
|
1327
1327
|
}
|
|
@@ -1331,7 +1331,7 @@ function install2(__dmScope) {
|
|
|
1331
1331
|
});
|
|
1332
1332
|
}
|
|
1333
1333
|
function publish(reason, extra) {
|
|
1334
|
-
var time =
|
|
1334
|
+
var time = finite3(audio.currentTime, 0);
|
|
1335
1335
|
__dmScope.dataset(state.root, "danceMovesCueTimeline", reason);
|
|
1336
1336
|
if (typeof options.render === "function")
|
|
1337
1337
|
options.render({ root: state.root, audio, time, previousTime: previous, reason, playing: !audio.paused && !audio.ended && !blocked(), active: activeAt(time), generation, detail: extra || null });
|
|
@@ -1341,10 +1341,10 @@ function install2(__dmScope) {
|
|
|
1341
1341
|
return;
|
|
1342
1342
|
fired.add(cue.id);
|
|
1343
1343
|
if (typeof options.onCue === "function")
|
|
1344
|
-
options.onCue({ root: state.root, audio, cue, time:
|
|
1344
|
+
options.onCue({ root: state.root, audio, cue, time: finite3(audio.currentTime, cue.time), reason, generation });
|
|
1345
1345
|
}
|
|
1346
1346
|
function rebuild(time, reason, allowLanding) {
|
|
1347
|
-
time = Math.max(0,
|
|
1347
|
+
time = Math.max(0, finite3(time, 0));
|
|
1348
1348
|
generation += 1;
|
|
1349
1349
|
fired.clear();
|
|
1350
1350
|
cues.forEach(function(cue) {
|
|
@@ -1370,7 +1370,7 @@ function install2(__dmScope) {
|
|
|
1370
1370
|
frame = 0;
|
|
1371
1371
|
if (audio.paused || audio.ended || blocked())
|
|
1372
1372
|
return stop(audio.ended ? "ended" : blocked() ? "inactive" : "paused");
|
|
1373
|
-
var now = Math.max(0,
|
|
1373
|
+
var now = Math.max(0, finite3(audio.currentTime, previous));
|
|
1374
1374
|
var delta = now - previous;
|
|
1375
1375
|
if (delta < -1e-3 || delta > maxCrossingGap)
|
|
1376
1376
|
rebuild(now, "discontinuity", false);
|
|
@@ -1379,8 +1379,8 @@ function install2(__dmScope) {
|
|
|
1379
1379
|
if (previous < cue.time && now >= cue.time)
|
|
1380
1380
|
fire(cue, "crossing");
|
|
1381
1381
|
});
|
|
1382
|
-
var wallDelta = previousWall ? Math.min(0.25, Math.max(0, (
|
|
1383
|
-
previousWall =
|
|
1382
|
+
var wallDelta = previousWall ? Math.min(0.25, Math.max(0, (finite3(wallNow, previousWall) - previousWall) / 1e3)) : 0;
|
|
1383
|
+
previousWall = finite3(wallNow, previousWall);
|
|
1384
1384
|
previous = now;
|
|
1385
1385
|
status = "playing";
|
|
1386
1386
|
publish("frame", { deltaSeconds: wallDelta });
|
|
@@ -1389,7 +1389,7 @@ function install2(__dmScope) {
|
|
|
1389
1389
|
function start(reason) {
|
|
1390
1390
|
if (frame || audio.paused || audio.ended || blocked())
|
|
1391
1391
|
return publish(blocked() ? "inactive" : audio.ended ? "ended" : "paused");
|
|
1392
|
-
previous = Math.max(0,
|
|
1392
|
+
previous = Math.max(0, finite3(audio.currentTime, 0));
|
|
1393
1393
|
previousWall = 0;
|
|
1394
1394
|
status = reason || "playing";
|
|
1395
1395
|
publish(status);
|
|
@@ -1453,7 +1453,7 @@ function install2(__dmScope) {
|
|
|
1453
1453
|
start,
|
|
1454
1454
|
stop,
|
|
1455
1455
|
snapshot: function() {
|
|
1456
|
-
return { id: state.id, type: "cue-timeline", status, time:
|
|
1456
|
+
return { id: state.id, type: "cue-timeline", status, time: finite3(audio.currentTime, 0), previousTime: previous, generation, fired: Array.from(fired), active: activeAt(finite3(audio.currentTime, 0)).map(function(cue) {
|
|
1457
1457
|
return cue.id;
|
|
1458
1458
|
}) };
|
|
1459
1459
|
},
|
|
@@ -1476,28 +1476,28 @@ function install2(__dmScope) {
|
|
|
1476
1476
|
if (!target)
|
|
1477
1477
|
throw new Error("DanceMovesEffects spritePlayback requires a target element");
|
|
1478
1478
|
var frameMap = Array.isArray(options.frameMap) && options.frameMap.length ? options.frameMap.map(function(value) {
|
|
1479
|
-
value = Math.floor(
|
|
1479
|
+
value = Math.floor(finite3(value, -1));
|
|
1480
1480
|
if (value < 0)
|
|
1481
1481
|
throw new TypeError("DanceMovesEffects spritePlayback frameMap values must be non-negative integers");
|
|
1482
1482
|
return value;
|
|
1483
1483
|
}) : null;
|
|
1484
|
-
var frameCount = frameMap ? frameMap.length : Math.max(1, Math.round(
|
|
1485
|
-
var columns = Math.max(1, Math.round(
|
|
1486
|
-
var framesPerRow = clamp(Math.round(
|
|
1487
|
-
var columnOffset = clamp(Math.round(
|
|
1488
|
-
var rowOffset = Math.max(0, Math.round(
|
|
1489
|
-
var cycleTicks = Math.max(1, Math.round(
|
|
1490
|
-
var phaseOffsetSeconds =
|
|
1491
|
-
var staticFrame = clamp(Math.round(
|
|
1484
|
+
var frameCount = frameMap ? frameMap.length : Math.max(1, Math.round(finite3(options.frameCount, 1)));
|
|
1485
|
+
var columns = Math.max(1, Math.round(finite3(options.columns, frameCount)));
|
|
1486
|
+
var framesPerRow = clamp(Math.round(finite3(options.framesPerRow, columns)), 1, columns);
|
|
1487
|
+
var columnOffset = clamp(Math.round(finite3(options.columnOffset, 0)), 0, columns - framesPerRow);
|
|
1488
|
+
var rowOffset = Math.max(0, Math.round(finite3(options.rowOffset, 0)));
|
|
1489
|
+
var cycleTicks = Math.max(1, Math.round(finite3(options.cycleTicks, 32)));
|
|
1490
|
+
var phaseOffsetSeconds = finite3(options.phaseOffsetSeconds, 0);
|
|
1491
|
+
var staticFrame = clamp(Math.round(finite3(options.staticFrame, 0)), 0, frameCount - 1);
|
|
1492
1492
|
var prefix = String(options.propertyPrefix || "--dance-moves-sprite").replace(/-+$/, "");
|
|
1493
1493
|
if (prefix.slice(0, 2) !== "--")
|
|
1494
1494
|
prefix = "--" + prefix.replace(/^-+/, "");
|
|
1495
1495
|
var tiers = Array.isArray(options.qualityTiers) && options.qualityTiers.length ? options.qualityTiers.map(String) : ["full", "constrained", "minimal"];
|
|
1496
1496
|
var suppliedFps = Array.isArray(options.qualityFramesPerSecond) ? options.qualityFramesPerSecond : [60, 30, 15];
|
|
1497
1497
|
var qualityFps = tiers.map(function(_, index) {
|
|
1498
|
-
return Math.max(1,
|
|
1498
|
+
return Math.max(1, finite3(suppliedFps[index], suppliedFps[suppliedFps.length - 1] || 15));
|
|
1499
1499
|
});
|
|
1500
|
-
var qualityIndex = clamp(Math.round(
|
|
1500
|
+
var qualityIndex = clamp(Math.round(finite3(options.initialQuality, 0)), 0, tiers.length - 1);
|
|
1501
1501
|
var frame = 0, status = "idle", lastWall = -Infinity, renders = 0, latest = null;
|
|
1502
1502
|
function blocked() {
|
|
1503
1503
|
return document2.hidden || state.reduced.matches || state.forced.matches;
|
|
@@ -1507,8 +1507,8 @@ function install2(__dmScope) {
|
|
|
1507
1507
|
return { sourceFrame, column: sourceFrame % columns, row: Math.floor(sourceFrame / columns) };
|
|
1508
1508
|
}
|
|
1509
1509
|
function calculate(reason, staticFallback) {
|
|
1510
|
-
var cycleSeconds = Math.max(1e-3,
|
|
1511
|
-
var time = Math.max(0,
|
|
1510
|
+
var cycleSeconds = Math.max(1e-3, finite3(motion.durationMilliseconds(cycleTicks), 1) / 1e3);
|
|
1511
|
+
var time = Math.max(0, finite3(audio.currentTime, 0));
|
|
1512
1512
|
var phase = ((time - phaseOffsetSeconds) % cycleSeconds + cycleSeconds) % cycleSeconds / cycleSeconds;
|
|
1513
1513
|
var localFrame = staticFallback ? staticFrame : Math.min(frameCount - 1, Math.floor(phase * frameCount + 1e-9));
|
|
1514
1514
|
var mapped = mappedFrame(localFrame);
|
|
@@ -1563,8 +1563,8 @@ function install2(__dmScope) {
|
|
|
1563
1563
|
if (audio.paused || audio.ended || blocked())
|
|
1564
1564
|
return stop(audio.ended ? "ended" : blocked() ? "inactive" : "paused", audio.ended || blocked());
|
|
1565
1565
|
var minimumInterval = 1e3 / qualityFps[qualityIndex];
|
|
1566
|
-
if (!Number.isFinite(lastWall) ||
|
|
1567
|
-
lastWall =
|
|
1566
|
+
if (!Number.isFinite(lastWall) || finite3(wallNow, lastWall) - lastWall >= minimumInterval - 0.01) {
|
|
1567
|
+
lastWall = finite3(wallNow, lastWall);
|
|
1568
1568
|
status = "playing";
|
|
1569
1569
|
publish("frame", false);
|
|
1570
1570
|
}
|
|
@@ -1595,7 +1595,7 @@ function install2(__dmScope) {
|
|
|
1595
1595
|
}
|
|
1596
1596
|
function setQuality(value, reason) {
|
|
1597
1597
|
var named = typeof value === "string" ? tiers.indexOf(value) : -1;
|
|
1598
|
-
qualityIndex = named >= 0 ? named : clamp(Math.round(
|
|
1598
|
+
qualityIndex = named >= 0 ? named : clamp(Math.round(finite3(value, qualityIndex)), 0, tiers.length - 1);
|
|
1599
1599
|
lastWall = -Infinity;
|
|
1600
1600
|
publish(reason || "quality-change", audio.ended || blocked());
|
|
1601
1601
|
}
|
|
@@ -1649,7 +1649,7 @@ function install2(__dmScope) {
|
|
|
1649
1649
|
},
|
|
1650
1650
|
setQuality,
|
|
1651
1651
|
snapshot: function() {
|
|
1652
|
-
return { id: state.id, type: "sprite-playback", status, time:
|
|
1652
|
+
return { id: state.id, type: "sprite-playback", status, time: finite3(audio.currentTime, 0), frame: latest ? latest.frame : null, localFrame: latest ? latest.localFrame : null, column: latest ? latest.column : null, row: latest ? latest.row : null, phase: latest ? latest.phase : null, cycleTicks, cycleSeconds: latest ? latest.cycleSeconds : null, qualityTier: tiers[qualityIndex], qualityIndex, renders, running: Boolean(frame) };
|
|
1653
1653
|
},
|
|
1654
1654
|
teardown: function() {
|
|
1655
1655
|
stop("teardown", true);
|
|
@@ -1716,8 +1716,8 @@ function install2(__dmScope) {
|
|
|
1716
1716
|
popover.appendChild(viewport);
|
|
1717
1717
|
__dmScope.dataset(popover, "danceMovesLyricStage", "ready");
|
|
1718
1718
|
__dmScope.setStyle(popover.style, "--dance-moves-lyric-progress", "0");
|
|
1719
|
-
var travelTicks = Math.max(1, Math.round(
|
|
1720
|
-
var defaultCueTicks = Math.max(1, Math.round(
|
|
1719
|
+
var travelTicks = Math.max(1, Math.round(finite3(options.travelTicks, 32)));
|
|
1720
|
+
var defaultCueTicks = Math.max(1, Math.round(finite3(options.cueDurationTicks, 32)));
|
|
1721
1721
|
var latest = null, phase = "idle", progress = 0, frame = 0, wakeTimer = 0, cueTimer = 0, cueCleanup = null, cueCount = 0;
|
|
1722
1722
|
var lyricEffects = [];
|
|
1723
1723
|
var slots = Object.freeze({ previous, current, next, viewport, track });
|
|
@@ -1766,7 +1766,7 @@ function install2(__dmScope) {
|
|
|
1766
1766
|
publish(reason || value);
|
|
1767
1767
|
}
|
|
1768
1768
|
function setProgress(value) {
|
|
1769
|
-
progress = clamp(
|
|
1769
|
+
progress = clamp(finite3(value, 0), 0, 1);
|
|
1770
1770
|
__dmScope.setStyle(popover.style, "--dance-moves-lyric-progress", progress.toFixed(5));
|
|
1771
1771
|
}
|
|
1772
1772
|
function copyLyrics(detail) {
|
|
@@ -1783,7 +1783,7 @@ function install2(__dmScope) {
|
|
|
1783
1783
|
var cleanup = typeof result === "function" ? result : typeof result.cleanup === "function" ? result.cleanup : null;
|
|
1784
1784
|
if (!cleanup)
|
|
1785
1785
|
return;
|
|
1786
|
-
var durationTicks = result && typeof result === "object" ? Math.max(1, Math.round(
|
|
1786
|
+
var durationTicks = result && typeof result === "object" ? Math.max(1, Math.round(finite3(result.durationTicks, 64))) : 64;
|
|
1787
1787
|
var record = { cleanup, timer: 0 };
|
|
1788
1788
|
record.timer = window2.setTimeout(function() {
|
|
1789
1789
|
lyricEffects = lyricEffects.filter(function(item) {
|
|
@@ -1809,7 +1809,7 @@ function install2(__dmScope) {
|
|
|
1809
1809
|
setProgress(0);
|
|
1810
1810
|
return setPhase("waiting", "no-next-line");
|
|
1811
1811
|
}
|
|
1812
|
-
var now = Math.max(0,
|
|
1812
|
+
var now = Math.max(0, finite3(audio.currentTime, 0));
|
|
1813
1813
|
if (now < windowRange.start)
|
|
1814
1814
|
return reconcile("frame-before-window");
|
|
1815
1815
|
if (now >= windowRange.end) {
|
|
@@ -1839,7 +1839,7 @@ function install2(__dmScope) {
|
|
|
1839
1839
|
setProgress(0);
|
|
1840
1840
|
return setPhase("waiting", reason || "no-next-line");
|
|
1841
1841
|
}
|
|
1842
|
-
var now = Math.max(0,
|
|
1842
|
+
var now = Math.max(0, finite3(audio.currentTime, 0));
|
|
1843
1843
|
if (now >= windowRange.end) {
|
|
1844
1844
|
setProgress(1);
|
|
1845
1845
|
return setPhase("arrived", reason || "arrived");
|
|
@@ -1854,7 +1854,7 @@ function install2(__dmScope) {
|
|
|
1854
1854
|
wakeTimer = window2.setTimeout(function() {
|
|
1855
1855
|
wakeTimer = 0;
|
|
1856
1856
|
tick();
|
|
1857
|
-
}, Math.max(0, (windowRange.start - now) * 1e3 / Math.max(0.01,
|
|
1857
|
+
}, Math.max(0, (windowRange.start - now) * 1e3 / Math.max(0.01, finite3(audio.playbackRate, 1))));
|
|
1858
1858
|
}
|
|
1859
1859
|
function onLyric(detail) {
|
|
1860
1860
|
var previousDetail = latest;
|
|
@@ -1909,7 +1909,7 @@ function install2(__dmScope) {
|
|
|
1909
1909
|
else if (result && typeof result === "object") {
|
|
1910
1910
|
if (typeof result.cleanup === "function")
|
|
1911
1911
|
cueCleanup = result.cleanup;
|
|
1912
|
-
durationTicks = Math.max(1, Math.round(
|
|
1912
|
+
durationTicks = Math.max(1, Math.round(finite3(result.durationTicks, defaultCueTicks)));
|
|
1913
1913
|
}
|
|
1914
1914
|
cueTimer = window2.setTimeout(function() {
|
|
1915
1915
|
clearCue("complete");
|
|
@@ -1981,16 +1981,16 @@ function install2(__dmScope) {
|
|
|
1981
1981
|
var hazardSources = Array.isArray(options.hazardSources) ? options.hazardSources.filter(Boolean).map(String) : [];
|
|
1982
1982
|
if (!shipSources.length || !hazardSources.length)
|
|
1983
1983
|
throw new Error("DanceMovesEffects cooperativeArena requires ship and hazard sources");
|
|
1984
|
-
var shipCount = clamp(Math.round(
|
|
1985
|
-
var hazardCount = clamp(Math.round(
|
|
1986
|
-
var speed = clamp(
|
|
1984
|
+
var shipCount = clamp(Math.round(finite3(options.shipCount, 3)), 2, 12);
|
|
1985
|
+
var hazardCount = clamp(Math.round(finite3(options.hazardCount, 8)), 3, 48);
|
|
1986
|
+
var speed = clamp(finite3(options.speed, 1), 0.35, 2.4);
|
|
1987
1987
|
var inlineTransform = options.inlineTransform === true;
|
|
1988
|
-
var bpm = Math.max(1,
|
|
1989
|
-
var shotIntervalTicks = clamp(Math.round(
|
|
1988
|
+
var bpm = Math.max(1, finite3(options.bpm, motion.bpm || 120));
|
|
1989
|
+
var shotIntervalTicks = clamp(Math.round(finite3(options.shotIntervalTicks, 32)), 4, 256);
|
|
1990
1990
|
var shotIntervalSeconds = motion.durationMilliseconds ? motion.durationMilliseconds(shotIntervalTicks, bpm) / 1e3 : shotIntervalTicks * 60 / (bpm * 16);
|
|
1991
|
-
var burstDurationSeconds = clamp(
|
|
1992
|
-
var shipHitboxScale = clamp(
|
|
1993
|
-
var hazardHitboxScale = clamp(
|
|
1991
|
+
var burstDurationSeconds = clamp(finite3(options.burstDurationBeats, 1.5), 0.1, 8) * 60 / bpm;
|
|
1992
|
+
var shipHitboxScale = clamp(finite3(options.shipHitboxScale, 1), 0.4, 1);
|
|
1993
|
+
var hazardHitboxScale = clamp(finite3(options.hazardHitboxScale, 1), 0.4, 1);
|
|
1994
1994
|
var removers = state.removers, ships = [], hazards = [], shots = [], bursts = [], burstTimers = [];
|
|
1995
1995
|
var activeShips = shipCount, activeHazards = hazardCount;
|
|
1996
1996
|
var width = 1, height = 1, frame = 0, last = 0, elapsed = 0, nextShot = 0, hits = 0;
|
|
@@ -2322,7 +2322,7 @@ function install2(__dmScope) {
|
|
|
2322
2322
|
reconcile(reason || "manual");
|
|
2323
2323
|
},
|
|
2324
2324
|
setDensity: function(nextShips, nextHazards) {
|
|
2325
|
-
var shipsNext = clamp(Math.round(
|
|
2325
|
+
var shipsNext = clamp(Math.round(finite3(nextShips, activeShips)), 2, shipCount), hazardsNext = clamp(Math.round(finite3(nextHazards, activeHazards)), 3, hazardCount);
|
|
2326
2326
|
if (shipsNext === activeShips && hazardsNext === activeHazards)
|
|
2327
2327
|
return;
|
|
2328
2328
|
activeShips = shipsNext;
|
|
@@ -2365,12 +2365,12 @@ function install2(__dmScope) {
|
|
|
2365
2365
|
options = options || {};
|
|
2366
2366
|
var state = commonState(options, "quality");
|
|
2367
2367
|
var tiers = Array.isArray(options.tiers) && options.tiers.length ? options.tiers.map(String) : ["full", "constrained", "minimal"];
|
|
2368
|
-
var index = clamp(Math.round(
|
|
2369
|
-
var sampleMs = Math.max(800,
|
|
2370
|
-
var poorWindows = Math.max(2, Math.round(
|
|
2371
|
-
var healthyWindows = Math.max(3, Math.round(
|
|
2372
|
-
var downgradeRatio = clamp(
|
|
2373
|
-
var recoveryRatio = clamp(
|
|
2368
|
+
var index = clamp(Math.round(finite3(options.initial, 0)), 0, tiers.length - 1);
|
|
2369
|
+
var sampleMs = Math.max(800, finite3(options.sampleMilliseconds, 1600));
|
|
2370
|
+
var poorWindows = Math.max(2, Math.round(finite3(options.poorWindows, 2)));
|
|
2371
|
+
var healthyWindows = Math.max(3, Math.round(finite3(options.healthyWindows, 5)));
|
|
2372
|
+
var downgradeRatio = clamp(finite3(options.downgradeRatio, 0.72), 0.2, 0.95);
|
|
2373
|
+
var recoveryRatio = clamp(finite3(options.recoveryRatio, 0.9), downgradeRatio, 1);
|
|
2374
2374
|
var frame = 0, frames = 0, start = 0, reference = 0, poor = 0, healthy = 0, lastShift = 0, running = true;
|
|
2375
2375
|
var preferenceLimited = false, tierBeforePreference = index;
|
|
2376
2376
|
function publish(reason, sample) {
|
|
@@ -2414,12 +2414,12 @@ function install2(__dmScope) {
|
|
|
2414
2414
|
poor = 0;
|
|
2415
2415
|
healthy = 0;
|
|
2416
2416
|
}
|
|
2417
|
-
if (poor >= poorWindows && index < tiers.length - 1 && now - lastShift >=
|
|
2417
|
+
if (poor >= poorWindows && index < tiers.length - 1 && now - lastShift >= finite3(options.downgradeCooldownMilliseconds, 5e3)) {
|
|
2418
2418
|
index += 1;
|
|
2419
2419
|
poor = 0;
|
|
2420
2420
|
lastShift = now;
|
|
2421
2421
|
publish("sustained-low-fps", { fps });
|
|
2422
|
-
} else if (healthy >= healthyWindows && index > 0 && now - lastShift >=
|
|
2422
|
+
} else if (healthy >= healthyWindows && index > 0 && now - lastShift >= finite3(options.recoveryCooldownMilliseconds, 12e3)) {
|
|
2423
2423
|
index -= 1;
|
|
2424
2424
|
healthy = 0;
|
|
2425
2425
|
lastShift = now;
|
|
@@ -2458,7 +2458,7 @@ function install2(__dmScope) {
|
|
|
2458
2458
|
id: state.id,
|
|
2459
2459
|
type: "quality",
|
|
2460
2460
|
setTier: function(value, reason) {
|
|
2461
|
-
index = clamp(Math.round(
|
|
2461
|
+
index = clamp(Math.round(finite3(value, index)), 0, tiers.length - 1);
|
|
2462
2462
|
publish(reason || "manual");
|
|
2463
2463
|
},
|
|
2464
2464
|
snapshot: function() {
|
|
@@ -6436,6 +6436,184 @@ function installPlayback(scope, api, host = scope.document.defaultView) {
|
|
|
6436
6436
|
});
|
|
6437
6437
|
}
|
|
6438
6438
|
|
|
6439
|
+
// .build/modules/recordings.mjs
|
|
6440
|
+
var WASM = "AGFzbQEAAAABDwJgAAF/YAZ/f39/f38BfAMDAgABBQUBAQKAQAYIAX8BQYCIBAsHKgMGbWVtb3J5AgAKdGFibGVfYmFzZQAAEHNhbXBsZV9yZWNvcmRpbmcAAQpvAggAQYCIhIAAC2QCAXwBfkQAAAAAAAD4fyEGAkAgAEUNACABQQFIDQAgAkEBSA0AIAVBAEgNACAFIAJPDQAgBKwgA6x9IgdCAFMNACAHIAGtWg0AIAAgB6cgAmxBA3RqIAVBA3RqKwMAIQYLIAYL";
|
|
6441
|
+
var MAX_VALUES = 8388608;
|
|
6442
|
+
var MAX_BYTES = 64 * 1024 * 1024;
|
|
6443
|
+
var modulePromise;
|
|
6444
|
+
var finite2 = (n, label) => {
|
|
6445
|
+
if (typeof n !== "number" || !Number.isFinite(n)) throw new TypeError(label + " must be finite");
|
|
6446
|
+
return n;
|
|
6447
|
+
};
|
|
6448
|
+
function validateRecording(input) {
|
|
6449
|
+
if (!input || input.format !== "dancerudiments.recording" || input.schema_version !== 1 || input.pips_per_beat !== 64 || input.sample_dtype !== "float64") throw new TypeError("Expected dancerudiments.recording v1, float64, 64 pips per beat");
|
|
6450
|
+
if (typeof input.name !== "string" || !input.name.length || input.name.length > 128) throw new TypeError("Recording name is required");
|
|
6451
|
+
const dimensions = input.dimensions;
|
|
6452
|
+
if (!Array.isArray(dimensions) || !dimensions.length || dimensions.length > 64 || new Set(dimensions).size !== dimensions.length || dimensions.some((d) => typeof d !== "string" || !/^[a-z][a-z0-9_]{0,127}$/.test(d))) throw new TypeError("Recording requires 1..64 unique named dimensions");
|
|
6453
|
+
const c = input.clock;
|
|
6454
|
+
if (!c || finite2(c.bpm, "Recording BPM") <= 0 || !["confirmed", "estimated"].includes(c.bpm_status) || !["unverified", "estimated", "supplied"].includes(c.phase_status) || typeof c.bpm_source !== "string" || !c.bpm_source.trim()) throw new TypeError("Recording clock requires BPM and provenance");
|
|
6455
|
+
finite2(c.beat_zero_seconds, "Beat zero");
|
|
6456
|
+
if (!Number.isInteger(c.start_pip) || c.start_pip < -2147483648) throw new RangeError("Invalid recording start pip");
|
|
6457
|
+
const rows = input.samples;
|
|
6458
|
+
if (!Array.isArray(rows) || !rows.length || rows.length * dimensions.length > MAX_VALUES || c.start_pip + rows.length > 2147483647) throw new RangeError("Recording exceeds bounded sample limits");
|
|
6459
|
+
const values = new Float64Array(rows.length * dimensions.length);
|
|
6460
|
+
rows.forEach((row, i) => {
|
|
6461
|
+
if (!Array.isArray(row) || row.length !== dimensions.length) throw new TypeError("Recording sample dimensions differ");
|
|
6462
|
+
row.forEach((v, j) => {
|
|
6463
|
+
finite2(v, "Sample");
|
|
6464
|
+
if (Math.abs(v) > 1) throw new RangeError("Recording samples must be in [-1,1]");
|
|
6465
|
+
values[i * dimensions.length + j] = v;
|
|
6466
|
+
});
|
|
6467
|
+
});
|
|
6468
|
+
return Object.freeze({ name: input.name, dimensions: Object.freeze([...dimensions]), count: rows.length, clock: Object.freeze({ ...c }), values });
|
|
6469
|
+
}
|
|
6470
|
+
async function samplerFromData(data) {
|
|
6471
|
+
if (!modulePromise) modulePromise = WebAssembly.compile(Uint8Array.from(atob(WASM), (c) => c.charCodeAt(0))).catch((error) => {
|
|
6472
|
+
modulePromise = null;
|
|
6473
|
+
throw error;
|
|
6474
|
+
});
|
|
6475
|
+
const api = (await WebAssembly.instantiate(await modulePromise, {})).exports;
|
|
6476
|
+
const base = api.table_base(), pages = Math.ceil((base + data.values.byteLength - api.memory.buffer.byteLength) / 65536);
|
|
6477
|
+
if (pages > 0) api.memory.grow(pages);
|
|
6478
|
+
new Float64Array(api.memory.buffer, base, data.values.length).set(data.values);
|
|
6479
|
+
return Object.freeze({ ...data, sample(pip) {
|
|
6480
|
+
if (!Number.isInteger(pip) || pip < data.clock.start_pip || pip >= data.clock.start_pip + data.count) throw new RangeError("Position is outside the non-looping recording");
|
|
6481
|
+
return data.dimensions.map((_, axis) => api.sample_recording(base, data.count, data.dimensions.length, data.clock.start_pip, pip, axis));
|
|
6482
|
+
} });
|
|
6483
|
+
}
|
|
6484
|
+
function createRecordingService(scope, rudiments, config) {
|
|
6485
|
+
const handles = /* @__PURE__ */ new Map();
|
|
6486
|
+
async function load(url2) {
|
|
6487
|
+
if (scope.disposed) throw new Error("DanceMoves mount is destroyed");
|
|
6488
|
+
const response = await scope.window.fetch(url2);
|
|
6489
|
+
if (!response.ok) throw new Error("Recording request failed: " + response.status);
|
|
6490
|
+
if (Number(response.headers?.get("content-length")) > MAX_BYTES) throw new RangeError("Recording exceeds 64 MiB");
|
|
6491
|
+
let text;
|
|
6492
|
+
if (response.body?.getReader) {
|
|
6493
|
+
const reader = response.body.getReader(), chunks = [];
|
|
6494
|
+
let size = 0;
|
|
6495
|
+
for (; ; ) {
|
|
6496
|
+
const { done, value } = await reader.read();
|
|
6497
|
+
if (done) break;
|
|
6498
|
+
size += value.byteLength;
|
|
6499
|
+
if (size > MAX_BYTES) {
|
|
6500
|
+
await reader.cancel();
|
|
6501
|
+
throw new RangeError("Recording exceeds 64 MiB");
|
|
6502
|
+
}
|
|
6503
|
+
chunks.push(value);
|
|
6504
|
+
}
|
|
6505
|
+
const bytes = new Uint8Array(size);
|
|
6506
|
+
let offset = 0;
|
|
6507
|
+
for (const chunk of chunks) {
|
|
6508
|
+
bytes.set(chunk, offset);
|
|
6509
|
+
offset += chunk.length;
|
|
6510
|
+
}
|
|
6511
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
6512
|
+
} else {
|
|
6513
|
+
text = await response.text();
|
|
6514
|
+
if (new TextEncoder().encode(text).length > MAX_BYTES) throw new RangeError("Recording exceeds 64 MiB");
|
|
6515
|
+
}
|
|
6516
|
+
const input = JSON.parse(text);
|
|
6517
|
+
validateRecording(input);
|
|
6518
|
+
return input;
|
|
6519
|
+
}
|
|
6520
|
+
function animate(options = {}) {
|
|
6521
|
+
if (scope.disposed) throw new Error("DanceMoves mount is destroyed");
|
|
6522
|
+
if (typeof options.id !== "string" || !options.id.trim() || handles.has(options.id)) throw new TypeError("Recording controller requires a unique id");
|
|
6523
|
+
if (typeof options.render !== "function") throw new TypeError("Recording render callback is required");
|
|
6524
|
+
if (!options.audio) throw new TypeError("Recording playback requires an audio/player source");
|
|
6525
|
+
if (["rate", "phasePips", "clock", "bpm"].some((k) => Object.hasOwn(options, k))) throw new TypeError("Recording clock, rate and origin come from its capture");
|
|
6526
|
+
const data = validateRecording(options.recording);
|
|
6527
|
+
if (Math.abs(data.clock.bpm - config.bpm) > 1e-9) throw new RangeError("Recording BPM does not match the authoritative page BPM");
|
|
6528
|
+
let inner = null, sampler = null, destroyed = false, enabled = options.enabled !== false, paused = false, status = "loading", error = null, last = null;
|
|
6529
|
+
const output = (frame, values, pip, reason) => {
|
|
6530
|
+
if (destroyed) return;
|
|
6531
|
+
last = { ...frame, rudiment: null, recording: data.name, dimensions: data.dimensions, values, position: { x: values[0] || 0, y: values[1] || 0, z: values[2] || 0 }, pip, positionPips: pip, loopProgress: null, reason };
|
|
6532
|
+
options.render(last);
|
|
6533
|
+
};
|
|
6534
|
+
const handle = {
|
|
6535
|
+
id: options.id,
|
|
6536
|
+
ready: null,
|
|
6537
|
+
setEnabled(value) {
|
|
6538
|
+
if (typeof value !== "boolean") throw new TypeError("enabled must be boolean");
|
|
6539
|
+
enabled = value;
|
|
6540
|
+
inner?.setEnabled(value);
|
|
6541
|
+
},
|
|
6542
|
+
pause() {
|
|
6543
|
+
paused = true;
|
|
6544
|
+
inner?.pause();
|
|
6545
|
+
},
|
|
6546
|
+
resume() {
|
|
6547
|
+
paused = false;
|
|
6548
|
+
inner?.resume();
|
|
6549
|
+
},
|
|
6550
|
+
refresh() {
|
|
6551
|
+
inner?.refresh();
|
|
6552
|
+
},
|
|
6553
|
+
destroy() {
|
|
6554
|
+
if (destroyed) return;
|
|
6555
|
+
inner?.destroy();
|
|
6556
|
+
inner = null;
|
|
6557
|
+
sampler = null;
|
|
6558
|
+
destroyed = true;
|
|
6559
|
+
status = "destroyed";
|
|
6560
|
+
handles.delete(options.id);
|
|
6561
|
+
},
|
|
6562
|
+
snapshot() {
|
|
6563
|
+
const state = inner?.snapshot() ?? null;
|
|
6564
|
+
return { id: options.id, type: "recording", recording: data.name, status: !destroyed && ["hidden", "offscreen", "paused", "seeking"].includes(state?.status) ? state.status : status, error, destroyed, enabled, paused, pip: last?.pip ?? null, dimensions: data.dimensions, count: data.count, clock: data.clock, inner: state };
|
|
6565
|
+
}
|
|
6566
|
+
};
|
|
6567
|
+
handle.teardown = handle.destroy;
|
|
6568
|
+
handles.set(options.id, handle);
|
|
6569
|
+
handle.ready = samplerFromData(data).then(async (native) => {
|
|
6570
|
+
if (destroyed || scope.disposed) return handle;
|
|
6571
|
+
sampler = native;
|
|
6572
|
+
inner = rudiments.animate({
|
|
6573
|
+
id: options.id,
|
|
6574
|
+
root: options.root,
|
|
6575
|
+
target: options.target,
|
|
6576
|
+
audio: options.audio,
|
|
6577
|
+
clock: "audio",
|
|
6578
|
+
rudiment: "circle",
|
|
6579
|
+
rate: 1,
|
|
6580
|
+
phasePips: 0,
|
|
6581
|
+
amplitude: 0,
|
|
6582
|
+
css: false,
|
|
6583
|
+
renderEveryFrame: true,
|
|
6584
|
+
offscreen: options.offscreen !== false,
|
|
6585
|
+
enabled,
|
|
6586
|
+
render(frame) {
|
|
6587
|
+
if (destroyed) return;
|
|
6588
|
+
const neutral = ["disabled", "reduced-motion", "forced-colors"].includes(frame.reason), precise = (frame.sourceBeats - data.clock.beat_zero_seconds * data.clock.bpm / 60) * 64;
|
|
6589
|
+
const pip = Math.floor(precise + 1e-8), outside = pip < data.clock.start_pip || pip >= data.clock.start_pip + data.count;
|
|
6590
|
+
status = neutral ? frame.reason : outside ? "outside-recording" : frame.reason;
|
|
6591
|
+
const values = neutral || outside ? data.dimensions.map(() => 0) : sampler.sample(pip);
|
|
6592
|
+
output(frame, values, pip, status);
|
|
6593
|
+
}
|
|
6594
|
+
});
|
|
6595
|
+
if (paused) inner.pause();
|
|
6596
|
+
await inner.ready;
|
|
6597
|
+
return handle;
|
|
6598
|
+
}).catch((failure) => {
|
|
6599
|
+
error = String(failure.message || failure);
|
|
6600
|
+
status = "error";
|
|
6601
|
+
inner?.destroy();
|
|
6602
|
+
inner = null;
|
|
6603
|
+
sampler = null;
|
|
6604
|
+
if (handles.get(options.id) === handle) handles.delete(options.id);
|
|
6605
|
+
throw failure;
|
|
6606
|
+
});
|
|
6607
|
+
return handle;
|
|
6608
|
+
}
|
|
6609
|
+
scope.onDispose(() => {
|
|
6610
|
+
for (const handle of [...handles.values()]) handle.destroy();
|
|
6611
|
+
});
|
|
6612
|
+
return Object.freeze({ load, animate, get: (id) => handles.get(id) || null, destroyAll: () => {
|
|
6613
|
+
for (const h of [...handles.values()]) h.destroy();
|
|
6614
|
+
}, snapshot: () => ({ native: "DanceRudiments 0.3.0 recording C++/WASM", instances: [...handles.values()].map((h) => h.snapshot()) }) });
|
|
6615
|
+
}
|
|
6616
|
+
|
|
6439
6617
|
// .build/modules/child-playback.mjs
|
|
6440
6618
|
var PROTOCOL = "dancemoves.child-playback.v1";
|
|
6441
6619
|
var EVENTS = /* @__PURE__ */ new Set(["snapshot", "loadedmetadata", "durationchange", "play", "playing", "pause", "ended", "seeking", "seeked", "ratechange", "timeupdate", "waiting", "error"]);
|
|
@@ -6860,7 +7038,7 @@ function installChildPlayback(scope, api, host) {
|
|
|
6860
7038
|
}
|
|
6861
7039
|
|
|
6862
7040
|
// .build/modules/version.mjs
|
|
6863
|
-
var VERSION = "3.
|
|
7041
|
+
var VERSION = "3.2.0";
|
|
6864
7042
|
|
|
6865
7043
|
// .build/modules/index.mjs
|
|
6866
7044
|
var mounted = /* @__PURE__ */ new WeakMap();
|
|
@@ -6913,6 +7091,7 @@ function createDanceMoves(options = {}) {
|
|
|
6913
7091
|
config,
|
|
6914
7092
|
effects: w.DanceMovesEffects,
|
|
6915
7093
|
rudiments: w.DanceMovesRudiments,
|
|
7094
|
+
recordings: createRecordingService(scope, w.DanceMovesRudiments, config),
|
|
6916
7095
|
orientationCore: w.KSEpkOrientationCore,
|
|
6917
7096
|
getOrientation: () => w.__ksEpkOrientationRuntime || null,
|
|
6918
7097
|
getClay: () => w.DanceMovesClayStars || null,
|
package/lib/index.d.ts
CHANGED
|
@@ -92,7 +92,7 @@ export interface DanceMoves {
|
|
|
92
92
|
resetRunningAnimations(audio?:PlaybackMedia):number;discoverAudio():void;registerPlaybackSource(source:PlaybackSource):void;unregisterPlaybackSource(source:PlaybackSource):void;
|
|
93
93
|
setDiagnosticsSink(callback:((record:Record<string,unknown>)=>void)|null):boolean;diagnosticsEnabled():boolean;
|
|
94
94
|
applyCatalogueTiming?():Record<string,unknown>;catalogueTimingSnapshot?():Record<string,unknown>;removeCatalogueAnimationScope?():void;
|
|
95
|
-
effects:Effects;rudiments:import('./rudiments').RudimentApi;
|
|
95
|
+
effects:Effects;rudiments:import('./rudiments').RudimentApi;recordings:RecordingService;
|
|
96
96
|
orientationCore:Record<string,(...args:any[])=>any>;
|
|
97
97
|
getOrientation():(Controller & {reset():void})|null;getClay():(Controller & Record<string,any>)|null;getPaperPlanes():Controller|null;
|
|
98
98
|
createOrientation(options:{render:(point:Offset & Record<string,unknown>)=>void;windowMilliseconds?:number;minimumSpanDegrees?:number;smoothingTimeConstantMilliseconds?:number}):OrientationController;
|
|
@@ -111,3 +111,7 @@ export function fromWordPressMeta(meta:Record<string,unknown>,resolveAttachment?
|
|
|
111
111
|
export interface Revision {revision:number;config:PageConfig;savedAt:string}
|
|
112
112
|
export interface PageStore {read(id:string):Revision|null;revisions(id:string):Revision[];save(id:string,input:PageConfig,options?:{expectedRevision?:number}):Revision;restore(id:string,revision:number,options?:{expectedRevision?:number}):Revision;delete(id:string):boolean}
|
|
113
113
|
export function createMemoryPageStore(options?:{maxPages?:number;maxRevisions?:number}):PageStore;
|
|
114
|
+
export interface AudioRecording {format:'dancerudiments.recording';schema_version:1;pips_per_beat:64;sample_dtype:'float64';name:string;dimensions:string[];samples:number[][];clock:{bpm:number;bpm_source:string;bpm_status:'confirmed'|'estimated';beat_zero_seconds:number;phase_status:'unverified'|'estimated'|'supplied';start_pip:number};provenance?:Record<string,unknown>}
|
|
115
|
+
export interface RecordingFrame {recording:string;dimensions:readonly string[];values:number[];position:{x:number;y:number;z:number};pip:number;positionPips:number;sourceBeats:number;loopProgress:null;reason:string}
|
|
116
|
+
export interface RecordingController {id:string;ready:Promise<RecordingController>;setEnabled(value:boolean):void;pause():void;resume():void;refresh():void;destroy():void;teardown():void;snapshot():Record<string,unknown>}
|
|
117
|
+
export interface RecordingService {load(url:string):Promise<AudioRecording>;animate(options:{id:string;root:HTMLElement;target:HTMLElement;audio:PlaybackMedia;recording:AudioRecording;offscreen?:boolean;enabled?:boolean;render:(frame:RecordingFrame)=>void}):RecordingController;get(id:string):RecordingController|null;destroyAll():void;snapshot():Record<string,unknown>}
|