@omote/core 0.9.3 → 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 +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +112 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +112 -83
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -2541,6 +2541,9 @@ declare class A2EProcessor {
|
|
|
2541
2541
|
private lastPulledFrame;
|
|
2542
2542
|
private lastDequeuedTime;
|
|
2543
2543
|
private decayBuffer;
|
|
2544
|
+
private smoother;
|
|
2545
|
+
private gapDecayStarted;
|
|
2546
|
+
private lastSmootherUpdate;
|
|
2544
2547
|
private inferenceRunning;
|
|
2545
2548
|
private pendingChunks;
|
|
2546
2549
|
private getFrameCallCount;
|
|
@@ -2660,6 +2663,15 @@ declare class BlendshapeSmoother {
|
|
|
2660
2663
|
* Springs will converge toward these values on subsequent update() calls.
|
|
2661
2664
|
*/
|
|
2662
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;
|
|
2663
2675
|
/**
|
|
2664
2676
|
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
2665
2677
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -2541,6 +2541,9 @@ declare class A2EProcessor {
|
|
|
2541
2541
|
private lastPulledFrame;
|
|
2542
2542
|
private lastDequeuedTime;
|
|
2543
2543
|
private decayBuffer;
|
|
2544
|
+
private smoother;
|
|
2545
|
+
private gapDecayStarted;
|
|
2546
|
+
private lastSmootherUpdate;
|
|
2544
2547
|
private inferenceRunning;
|
|
2545
2548
|
private pendingChunks;
|
|
2546
2549
|
private getFrameCallCount;
|
|
@@ -2660,6 +2663,15 @@ declare class BlendshapeSmoother {
|
|
|
2660
2663
|
* Springs will converge toward these values on subsequent update() calls.
|
|
2661
2664
|
*/
|
|
2662
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;
|
|
2663
2675
|
/**
|
|
2664
2676
|
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
2665
2677
|
*
|
package/dist/index.js
CHANGED
|
@@ -1687,12 +1687,99 @@ var AudioChunkCoalescer = class {
|
|
|
1687
1687
|
}
|
|
1688
1688
|
};
|
|
1689
1689
|
|
|
1690
|
+
// src/inference/BlendshapeSmoother.ts
|
|
1691
|
+
var NUM_BLENDSHAPES = 52;
|
|
1692
|
+
var BlendshapeSmoother = class {
|
|
1693
|
+
constructor(config) {
|
|
1694
|
+
/** Whether any target has been set */
|
|
1695
|
+
this._hasTarget = false;
|
|
1696
|
+
this.halflife = config?.halflife ?? 0.06;
|
|
1697
|
+
this.values = new Float32Array(NUM_BLENDSHAPES);
|
|
1698
|
+
this.velocities = new Float32Array(NUM_BLENDSHAPES);
|
|
1699
|
+
this.targets = new Float32Array(NUM_BLENDSHAPES);
|
|
1700
|
+
}
|
|
1701
|
+
/** Whether a target frame has been set (false until first setTarget call) */
|
|
1702
|
+
get hasTarget() {
|
|
1703
|
+
return this._hasTarget;
|
|
1704
|
+
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Set new target frame from inference output.
|
|
1707
|
+
* Springs will converge toward these values on subsequent update() calls.
|
|
1708
|
+
*/
|
|
1709
|
+
setTarget(frame) {
|
|
1710
|
+
this.targets.set(frame);
|
|
1711
|
+
this._hasTarget = true;
|
|
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
|
+
}
|
|
1726
|
+
/**
|
|
1727
|
+
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
1728
|
+
*
|
|
1729
|
+
* Call this every render frame (e.g., inside requestAnimationFrame).
|
|
1730
|
+
* Returns the internal values buffer — do NOT mutate the returned array.
|
|
1731
|
+
*
|
|
1732
|
+
* @param dt - Time step in seconds (e.g., 1/60 for 60fps)
|
|
1733
|
+
* @returns Smoothed blendshape values (Float32Array of 52)
|
|
1734
|
+
*/
|
|
1735
|
+
update(dt) {
|
|
1736
|
+
if (!this._hasTarget) {
|
|
1737
|
+
return this.values;
|
|
1738
|
+
}
|
|
1739
|
+
if (this.halflife <= 0) {
|
|
1740
|
+
this.values.set(this.targets);
|
|
1741
|
+
this.velocities.fill(0);
|
|
1742
|
+
return this.values;
|
|
1743
|
+
}
|
|
1744
|
+
const damping = Math.LN2 / this.halflife;
|
|
1745
|
+
const eydt = Math.exp(-damping * dt);
|
|
1746
|
+
for (let i = 0; i < NUM_BLENDSHAPES; i++) {
|
|
1747
|
+
const j0 = this.values[i] - this.targets[i];
|
|
1748
|
+
const j1 = this.velocities[i] + j0 * damping;
|
|
1749
|
+
this.values[i] = eydt * (j0 + j1 * dt) + this.targets[i];
|
|
1750
|
+
this.velocities[i] = eydt * (this.velocities[i] - j1 * damping * dt);
|
|
1751
|
+
this.values[i] = Math.max(0, Math.min(1, this.values[i]));
|
|
1752
|
+
}
|
|
1753
|
+
return this.values;
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Decay all spring targets to neutral (0).
|
|
1757
|
+
*
|
|
1758
|
+
* Call when inference stalls (no new frames for threshold duration).
|
|
1759
|
+
* The springs will smoothly close the mouth / relax the face over
|
|
1760
|
+
* the halflife period rather than freezing.
|
|
1761
|
+
*/
|
|
1762
|
+
decayToNeutral() {
|
|
1763
|
+
this.targets.fill(0);
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Reset all state (values, velocities, targets).
|
|
1767
|
+
* Call when starting a new playback session.
|
|
1768
|
+
*/
|
|
1769
|
+
reset() {
|
|
1770
|
+
this.values.fill(0);
|
|
1771
|
+
this.velocities.fill(0);
|
|
1772
|
+
this.targets.fill(0);
|
|
1773
|
+
this._hasTarget = false;
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1776
|
+
|
|
1690
1777
|
// src/inference/A2EProcessor.ts
|
|
1691
1778
|
var logger4 = createLogger("A2EProcessor");
|
|
1692
1779
|
var FRAME_RATE = 30;
|
|
1693
1780
|
var DRIP_INTERVAL_MS = 33;
|
|
1694
1781
|
var HOLD_DURATION_MS = 400;
|
|
1695
|
-
var
|
|
1782
|
+
var GAP_DECAY_HALFLIFE_S = 0.08;
|
|
1696
1783
|
var _A2EProcessor = class _A2EProcessor {
|
|
1697
1784
|
constructor(config) {
|
|
1698
1785
|
this.writeOffset = 0;
|
|
@@ -1707,6 +1794,8 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1707
1794
|
this.lastPulledFrame = null;
|
|
1708
1795
|
this.lastDequeuedTime = 0;
|
|
1709
1796
|
this.decayBuffer = null;
|
|
1797
|
+
this.gapDecayStarted = false;
|
|
1798
|
+
this.lastSmootherUpdate = 0;
|
|
1710
1799
|
// Inference serialization
|
|
1711
1800
|
this.inferenceRunning = false;
|
|
1712
1801
|
this.pendingChunks = [];
|
|
@@ -1721,6 +1810,7 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1721
1810
|
this.onError = config.onError;
|
|
1722
1811
|
this.bufferCapacity = this.chunkSize * 2;
|
|
1723
1812
|
this.buffer = new Float32Array(this.bufferCapacity);
|
|
1813
|
+
this.smoother = new BlendshapeSmoother({ halflife: GAP_DECAY_HALFLIFE_S });
|
|
1724
1814
|
}
|
|
1725
1815
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1726
1816
|
// Audio Input
|
|
@@ -1823,6 +1913,9 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1823
1913
|
this.pendingChunks = [];
|
|
1824
1914
|
this.inferenceRunning = false;
|
|
1825
1915
|
this.getFrameCallCount = 0;
|
|
1916
|
+
this.smoother.reset();
|
|
1917
|
+
this.gapDecayStarted = false;
|
|
1918
|
+
this.lastSmootherUpdate = 0;
|
|
1826
1919
|
}
|
|
1827
1920
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1828
1921
|
// Frame Output — Pull Mode (TTS playback)
|
|
@@ -1857,6 +1950,7 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1857
1950
|
const { frame } = this.timestampedQueue.shift();
|
|
1858
1951
|
this.lastPulledFrame = frame;
|
|
1859
1952
|
this.lastDequeuedTime = getClock().now();
|
|
1953
|
+
this.gapDecayStarted = false;
|
|
1860
1954
|
return frame;
|
|
1861
1955
|
}
|
|
1862
1956
|
if (this.timestampedQueue.length > 0 && this.getFrameCallCount % 60 === 0) {
|
|
@@ -1868,21 +1962,30 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
1868
1962
|
});
|
|
1869
1963
|
}
|
|
1870
1964
|
if (this.lastPulledFrame) {
|
|
1871
|
-
const
|
|
1965
|
+
const now = getClock().now();
|
|
1966
|
+
const elapsed = now - this.lastDequeuedTime;
|
|
1872
1967
|
if (elapsed < HOLD_DURATION_MS) {
|
|
1873
1968
|
return this.lastPulledFrame;
|
|
1874
1969
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1970
|
+
if (!this.gapDecayStarted) {
|
|
1971
|
+
this.smoother.setPosition(this.lastPulledFrame);
|
|
1972
|
+
this.smoother.decayToNeutral();
|
|
1973
|
+
this.gapDecayStarted = true;
|
|
1974
|
+
this.lastSmootherUpdate = now;
|
|
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) {
|
|
1877
1984
|
this.lastPulledFrame = null;
|
|
1878
1985
|
return null;
|
|
1879
1986
|
}
|
|
1880
|
-
const t = decayElapsed / DECAY_DURATION_MS;
|
|
1881
|
-
const factor = (1 - t) * (1 - t);
|
|
1882
1987
|
if (!this.decayBuffer) this.decayBuffer = new Float32Array(52);
|
|
1883
|
-
|
|
1884
|
-
this.decayBuffer[i] = this.lastPulledFrame[i] * factor;
|
|
1885
|
-
}
|
|
1988
|
+
this.decayBuffer.set(smoothed);
|
|
1886
1989
|
return this.decayBuffer;
|
|
1887
1990
|
}
|
|
1888
1991
|
return null;
|
|
@@ -11216,80 +11319,6 @@ var InterruptionHandler = class extends EventEmitter {
|
|
|
11216
11319
|
}
|
|
11217
11320
|
};
|
|
11218
11321
|
|
|
11219
|
-
// src/inference/BlendshapeSmoother.ts
|
|
11220
|
-
var NUM_BLENDSHAPES = 52;
|
|
11221
|
-
var BlendshapeSmoother = class {
|
|
11222
|
-
constructor(config) {
|
|
11223
|
-
/** Whether any target has been set */
|
|
11224
|
-
this._hasTarget = false;
|
|
11225
|
-
this.halflife = config?.halflife ?? 0.06;
|
|
11226
|
-
this.values = new Float32Array(NUM_BLENDSHAPES);
|
|
11227
|
-
this.velocities = new Float32Array(NUM_BLENDSHAPES);
|
|
11228
|
-
this.targets = new Float32Array(NUM_BLENDSHAPES);
|
|
11229
|
-
}
|
|
11230
|
-
/** Whether a target frame has been set (false until first setTarget call) */
|
|
11231
|
-
get hasTarget() {
|
|
11232
|
-
return this._hasTarget;
|
|
11233
|
-
}
|
|
11234
|
-
/**
|
|
11235
|
-
* Set new target frame from inference output.
|
|
11236
|
-
* Springs will converge toward these values on subsequent update() calls.
|
|
11237
|
-
*/
|
|
11238
|
-
setTarget(frame) {
|
|
11239
|
-
this.targets.set(frame);
|
|
11240
|
-
this._hasTarget = true;
|
|
11241
|
-
}
|
|
11242
|
-
/**
|
|
11243
|
-
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
11244
|
-
*
|
|
11245
|
-
* Call this every render frame (e.g., inside requestAnimationFrame).
|
|
11246
|
-
* Returns the internal values buffer — do NOT mutate the returned array.
|
|
11247
|
-
*
|
|
11248
|
-
* @param dt - Time step in seconds (e.g., 1/60 for 60fps)
|
|
11249
|
-
* @returns Smoothed blendshape values (Float32Array of 52)
|
|
11250
|
-
*/
|
|
11251
|
-
update(dt) {
|
|
11252
|
-
if (!this._hasTarget) {
|
|
11253
|
-
return this.values;
|
|
11254
|
-
}
|
|
11255
|
-
if (this.halflife <= 0) {
|
|
11256
|
-
this.values.set(this.targets);
|
|
11257
|
-
this.velocities.fill(0);
|
|
11258
|
-
return this.values;
|
|
11259
|
-
}
|
|
11260
|
-
const damping = Math.LN2 / this.halflife;
|
|
11261
|
-
const eydt = Math.exp(-damping * dt);
|
|
11262
|
-
for (let i = 0; i < NUM_BLENDSHAPES; i++) {
|
|
11263
|
-
const j0 = this.values[i] - this.targets[i];
|
|
11264
|
-
const j1 = this.velocities[i] + j0 * damping;
|
|
11265
|
-
this.values[i] = eydt * (j0 + j1 * dt) + this.targets[i];
|
|
11266
|
-
this.velocities[i] = eydt * (this.velocities[i] - j1 * damping * dt);
|
|
11267
|
-
this.values[i] = Math.max(0, Math.min(1, this.values[i]));
|
|
11268
|
-
}
|
|
11269
|
-
return this.values;
|
|
11270
|
-
}
|
|
11271
|
-
/**
|
|
11272
|
-
* Decay all spring targets to neutral (0).
|
|
11273
|
-
*
|
|
11274
|
-
* Call when inference stalls (no new frames for threshold duration).
|
|
11275
|
-
* The springs will smoothly close the mouth / relax the face over
|
|
11276
|
-
* the halflife period rather than freezing.
|
|
11277
|
-
*/
|
|
11278
|
-
decayToNeutral() {
|
|
11279
|
-
this.targets.fill(0);
|
|
11280
|
-
}
|
|
11281
|
-
/**
|
|
11282
|
-
* Reset all state (values, velocities, targets).
|
|
11283
|
-
* Call when starting a new playback session.
|
|
11284
|
-
*/
|
|
11285
|
-
reset() {
|
|
11286
|
-
this.values.fill(0);
|
|
11287
|
-
this.velocities.fill(0);
|
|
11288
|
-
this.targets.fill(0);
|
|
11289
|
-
this._hasTarget = false;
|
|
11290
|
-
}
|
|
11291
|
-
};
|
|
11292
|
-
|
|
11293
11322
|
// src/inference/SafariSpeechRecognition.ts
|
|
11294
11323
|
var logger33 = createLogger("SafariSpeech");
|
|
11295
11324
|
var SafariSpeechRecognition = class _SafariSpeechRecognition {
|