@omote/core 0.9.4 → 0.9.5
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/dist/index.d.mts +14 -10
- package/dist/index.d.ts +14 -10
- package/dist/index.js +54 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +54 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -2517,12 +2517,6 @@ interface A2EProcessorConfig {
|
|
|
2517
2517
|
* different expression intensity across face regions (brows, eyes, cheeks).
|
|
2518
2518
|
*/
|
|
2519
2519
|
identityIndex?: number;
|
|
2520
|
-
/**
|
|
2521
|
-
* Spring halflife for blendshape smoothing (seconds).
|
|
2522
|
-
* - 0.06 (default): Sweet spot for lip sync
|
|
2523
|
-
* - 0: Bypass (raw frames, no smoothing)
|
|
2524
|
-
*/
|
|
2525
|
-
smoothingHalflife?: number;
|
|
2526
2520
|
/** Callback fired with each blendshape frame (push mode) */
|
|
2527
2521
|
onFrame?: (frame: Float32Array) => void;
|
|
2528
2522
|
/** Error callback */
|
|
@@ -2544,11 +2538,12 @@ declare class A2EProcessor {
|
|
|
2544
2538
|
private plainQueue;
|
|
2545
2539
|
private _latestFrame;
|
|
2546
2540
|
private dripInterval;
|
|
2547
|
-
private
|
|
2541
|
+
private lastPulledFrame;
|
|
2548
2542
|
private lastDequeuedTime;
|
|
2549
|
-
private
|
|
2550
|
-
private
|
|
2551
|
-
private
|
|
2543
|
+
private decayBuffer;
|
|
2544
|
+
private smoother;
|
|
2545
|
+
private gapDecayStarted;
|
|
2546
|
+
private lastSmootherUpdate;
|
|
2552
2547
|
private inferenceRunning;
|
|
2553
2548
|
private pendingChunks;
|
|
2554
2549
|
private getFrameCallCount;
|
|
@@ -2668,6 +2663,15 @@ declare class BlendshapeSmoother {
|
|
|
2668
2663
|
* Springs will converge toward these values on subsequent update() calls.
|
|
2669
2664
|
*/
|
|
2670
2665
|
setTarget(frame: Float32Array): void;
|
|
2666
|
+
/**
|
|
2667
|
+
* Snap current position to a frame without triggering spring physics.
|
|
2668
|
+
* Zeroes velocities so the spring starts from rest at this position.
|
|
2669
|
+
*
|
|
2670
|
+
* Used by A2EProcessor to seed the smoother when entering gap decay —
|
|
2671
|
+
* positions the spring at the last real inference frame before decaying
|
|
2672
|
+
* toward neutral.
|
|
2673
|
+
*/
|
|
2674
|
+
setPosition(frame: Float32Array): void;
|
|
2671
2675
|
/**
|
|
2672
2676
|
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
2673
2677
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -2517,12 +2517,6 @@ interface A2EProcessorConfig {
|
|
|
2517
2517
|
* different expression intensity across face regions (brows, eyes, cheeks).
|
|
2518
2518
|
*/
|
|
2519
2519
|
identityIndex?: number;
|
|
2520
|
-
/**
|
|
2521
|
-
* Spring halflife for blendshape smoothing (seconds).
|
|
2522
|
-
* - 0.06 (default): Sweet spot for lip sync
|
|
2523
|
-
* - 0: Bypass (raw frames, no smoothing)
|
|
2524
|
-
*/
|
|
2525
|
-
smoothingHalflife?: number;
|
|
2526
2520
|
/** Callback fired with each blendshape frame (push mode) */
|
|
2527
2521
|
onFrame?: (frame: Float32Array) => void;
|
|
2528
2522
|
/** Error callback */
|
|
@@ -2544,11 +2538,12 @@ declare class A2EProcessor {
|
|
|
2544
2538
|
private plainQueue;
|
|
2545
2539
|
private _latestFrame;
|
|
2546
2540
|
private dripInterval;
|
|
2547
|
-
private
|
|
2541
|
+
private lastPulledFrame;
|
|
2548
2542
|
private lastDequeuedTime;
|
|
2549
|
-
private
|
|
2550
|
-
private
|
|
2551
|
-
private
|
|
2543
|
+
private decayBuffer;
|
|
2544
|
+
private smoother;
|
|
2545
|
+
private gapDecayStarted;
|
|
2546
|
+
private lastSmootherUpdate;
|
|
2552
2547
|
private inferenceRunning;
|
|
2553
2548
|
private pendingChunks;
|
|
2554
2549
|
private getFrameCallCount;
|
|
@@ -2668,6 +2663,15 @@ declare class BlendshapeSmoother {
|
|
|
2668
2663
|
* Springs will converge toward these values on subsequent update() calls.
|
|
2669
2664
|
*/
|
|
2670
2665
|
setTarget(frame: Float32Array): void;
|
|
2666
|
+
/**
|
|
2667
|
+
* Snap current position to a frame without triggering spring physics.
|
|
2668
|
+
* Zeroes velocities so the spring starts from rest at this position.
|
|
2669
|
+
*
|
|
2670
|
+
* Used by A2EProcessor to seed the smoother when entering gap decay —
|
|
2671
|
+
* positions the spring at the last real inference frame before decaying
|
|
2672
|
+
* toward neutral.
|
|
2673
|
+
*/
|
|
2674
|
+
setPosition(frame: Float32Array): void;
|
|
2671
2675
|
/**
|
|
2672
2676
|
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
2673
2677
|
*
|
package/dist/index.js
CHANGED
|
@@ -1710,6 +1710,19 @@ var BlendshapeSmoother = class {
|
|
|
1710
1710
|
this.targets.set(frame);
|
|
1711
1711
|
this._hasTarget = true;
|
|
1712
1712
|
}
|
|
1713
|
+
/**
|
|
1714
|
+
* Snap current position to a frame without triggering spring physics.
|
|
1715
|
+
* Zeroes velocities so the spring starts from rest at this position.
|
|
1716
|
+
*
|
|
1717
|
+
* Used by A2EProcessor to seed the smoother when entering gap decay —
|
|
1718
|
+
* positions the spring at the last real inference frame before decaying
|
|
1719
|
+
* toward neutral.
|
|
1720
|
+
*/
|
|
1721
|
+
setPosition(frame) {
|
|
1722
|
+
this.values.set(frame);
|
|
1723
|
+
this.velocities.fill(0);
|
|
1724
|
+
this._hasTarget = true;
|
|
1725
|
+
}
|
|
1713
1726
|
/**
|
|
1714
1727
|
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
1715
1728
|
*
|
|
@@ -1765,7 +1778,8 @@ var BlendshapeSmoother = class {
|
|
|
1765
1778
|
var logger4 = createLogger("A2EProcessor");
|
|
1766
1779
|
var FRAME_RATE = 30;
|
|
1767
1780
|
var DRIP_INTERVAL_MS = 33;
|
|
1768
|
-
var
|
|
1781
|
+
var HOLD_DURATION_MS = 400;
|
|
1782
|
+
var GAP_DECAY_HALFLIFE_S = 0.08;
|
|
1769
1783
|
var _A2EProcessor = class _A2EProcessor {
|
|
1770
1784
|
constructor(config) {
|
|
1771
1785
|
this.writeOffset = 0;
|
|
@@ -1776,10 +1790,12 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1776
1790
|
// Push mode state
|
|
1777
1791
|
this._latestFrame = null;
|
|
1778
1792
|
this.dripInterval = null;
|
|
1793
|
+
// Last-frame-hold for pull mode (prevents avatar freezing between frames)
|
|
1794
|
+
this.lastPulledFrame = null;
|
|
1779
1795
|
this.lastDequeuedTime = 0;
|
|
1780
|
-
this.
|
|
1781
|
-
this.
|
|
1782
|
-
this.
|
|
1796
|
+
this.decayBuffer = null;
|
|
1797
|
+
this.gapDecayStarted = false;
|
|
1798
|
+
this.lastSmootherUpdate = 0;
|
|
1783
1799
|
// Inference serialization
|
|
1784
1800
|
this.inferenceRunning = false;
|
|
1785
1801
|
this.pendingChunks = [];
|
|
@@ -1792,9 +1808,9 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1792
1808
|
this.identityIndex = config.identityIndex ?? 0;
|
|
1793
1809
|
this.onFrame = config.onFrame;
|
|
1794
1810
|
this.onError = config.onError;
|
|
1795
|
-
this.smoother = new BlendshapeSmoother({ halflife: config.smoothingHalflife ?? 0.06 });
|
|
1796
1811
|
this.bufferCapacity = this.chunkSize * 2;
|
|
1797
1812
|
this.buffer = new Float32Array(this.bufferCapacity);
|
|
1813
|
+
this.smoother = new BlendshapeSmoother({ halflife: GAP_DECAY_HALFLIFE_S });
|
|
1798
1814
|
}
|
|
1799
1815
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1800
1816
|
// Audio Input
|
|
@@ -1892,14 +1908,14 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1892
1908
|
this.timestampedQueue = [];
|
|
1893
1909
|
this.plainQueue = [];
|
|
1894
1910
|
this._latestFrame = null;
|
|
1895
|
-
this.
|
|
1911
|
+
this.lastPulledFrame = null;
|
|
1896
1912
|
this.lastDequeuedTime = 0;
|
|
1897
|
-
this.lastUpdateTime = 0;
|
|
1898
|
-
this.neutralTriggered = false;
|
|
1899
|
-
this.outputBuffer = null;
|
|
1900
1913
|
this.pendingChunks = [];
|
|
1901
1914
|
this.inferenceRunning = false;
|
|
1902
1915
|
this.getFrameCallCount = 0;
|
|
1916
|
+
this.smoother.reset();
|
|
1917
|
+
this.gapDecayStarted = false;
|
|
1918
|
+
this.lastSmootherUpdate = 0;
|
|
1903
1919
|
}
|
|
1904
1920
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1905
1921
|
// Frame Output — Pull Mode (TTS playback)
|
|
@@ -1915,7 +1931,6 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1915
1931
|
*/
|
|
1916
1932
|
getFrameForTime(currentTime) {
|
|
1917
1933
|
this.getFrameCallCount++;
|
|
1918
|
-
const now = getClock().now();
|
|
1919
1934
|
const discardWindow = this.backend.backend === "wasm" ? 1.5 : 0.5;
|
|
1920
1935
|
let discardCount = 0;
|
|
1921
1936
|
while (this.timestampedQueue.length > 0 && this.timestampedQueue[0].timestamp < currentTime - discardWindow) {
|
|
@@ -1931,20 +1946,14 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1931
1946
|
nextFrameTs: this.timestampedQueue.length > 0 ? this.timestampedQueue[0].timestamp.toFixed(3) : "none"
|
|
1932
1947
|
});
|
|
1933
1948
|
}
|
|
1934
|
-
let newDequeue = false;
|
|
1935
1949
|
if (this.timestampedQueue.length > 0 && this.timestampedQueue[0].timestamp <= currentTime) {
|
|
1936
1950
|
const { frame } = this.timestampedQueue.shift();
|
|
1937
|
-
|
|
1938
|
-
this.
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
this.lastUpdateTime = now;
|
|
1942
|
-
}
|
|
1943
|
-
newDequeue = true;
|
|
1944
|
-
this.neutralTriggered = false;
|
|
1945
|
-
this.lastDequeuedTime = now;
|
|
1951
|
+
this.lastPulledFrame = frame;
|
|
1952
|
+
this.lastDequeuedTime = getClock().now();
|
|
1953
|
+
this.gapDecayStarted = false;
|
|
1954
|
+
return frame;
|
|
1946
1955
|
}
|
|
1947
|
-
if (
|
|
1956
|
+
if (this.timestampedQueue.length > 0 && this.getFrameCallCount % 60 === 0) {
|
|
1948
1957
|
logger4.debug("getFrameForTime: waiting for playback time to reach queued frames", {
|
|
1949
1958
|
queueLen: this.timestampedQueue.length,
|
|
1950
1959
|
frontTimestamp: this.timestampedQueue[0].timestamp.toFixed(4),
|
|
@@ -1952,24 +1961,34 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1952
1961
|
delta: (this.timestampedQueue[0].timestamp - currentTime).toFixed(4)
|
|
1953
1962
|
});
|
|
1954
1963
|
}
|
|
1955
|
-
if (
|
|
1964
|
+
if (this.lastPulledFrame) {
|
|
1965
|
+
const now = getClock().now();
|
|
1956
1966
|
const elapsed = now - this.lastDequeuedTime;
|
|
1957
|
-
if (elapsed
|
|
1967
|
+
if (elapsed < HOLD_DURATION_MS) {
|
|
1968
|
+
return this.lastPulledFrame;
|
|
1969
|
+
}
|
|
1970
|
+
if (!this.gapDecayStarted) {
|
|
1971
|
+
this.smoother.setPosition(this.lastPulledFrame);
|
|
1958
1972
|
this.smoother.decayToNeutral();
|
|
1959
|
-
this.
|
|
1973
|
+
this.gapDecayStarted = true;
|
|
1974
|
+
this.lastSmootherUpdate = now;
|
|
1960
1975
|
}
|
|
1976
|
+
const dt = Math.min((now - this.lastSmootherUpdate) / 1e3, 0.1);
|
|
1977
|
+
this.lastSmootherUpdate = now;
|
|
1978
|
+
const smoothed = this.smoother.update(dt);
|
|
1979
|
+
let maxVal = 0;
|
|
1980
|
+
for (let i = 0; i < 52; i++) {
|
|
1981
|
+
if (smoothed[i] > maxVal) maxVal = smoothed[i];
|
|
1982
|
+
}
|
|
1983
|
+
if (maxVal < 1e-3) {
|
|
1984
|
+
this.lastPulledFrame = null;
|
|
1985
|
+
return null;
|
|
1986
|
+
}
|
|
1987
|
+
if (!this.decayBuffer) this.decayBuffer = new Float32Array(52);
|
|
1988
|
+
this.decayBuffer.set(smoothed);
|
|
1989
|
+
return this.decayBuffer;
|
|
1961
1990
|
}
|
|
1962
|
-
|
|
1963
|
-
return null;
|
|
1964
|
-
}
|
|
1965
|
-
const dt = Math.min((now - this.lastUpdateTime) / 1e3, 0.1);
|
|
1966
|
-
const smoothed = this.smoother.update(dt);
|
|
1967
|
-
this.lastUpdateTime = now;
|
|
1968
|
-
if (newDequeue || !this.outputBuffer) {
|
|
1969
|
-
this.outputBuffer = new Float32Array(52);
|
|
1970
|
-
}
|
|
1971
|
-
this.outputBuffer.set(smoothed);
|
|
1972
|
-
return this.outputBuffer;
|
|
1991
|
+
return null;
|
|
1973
1992
|
}
|
|
1974
1993
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1975
1994
|
// Frame Output — Push Mode (live mic, game loop)
|