@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.mjs
CHANGED
|
@@ -631,6 +631,19 @@ var BlendshapeSmoother = class {
|
|
|
631
631
|
this.targets.set(frame);
|
|
632
632
|
this._hasTarget = true;
|
|
633
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* Snap current position to a frame without triggering spring physics.
|
|
636
|
+
* Zeroes velocities so the spring starts from rest at this position.
|
|
637
|
+
*
|
|
638
|
+
* Used by A2EProcessor to seed the smoother when entering gap decay —
|
|
639
|
+
* positions the spring at the last real inference frame before decaying
|
|
640
|
+
* toward neutral.
|
|
641
|
+
*/
|
|
642
|
+
setPosition(frame) {
|
|
643
|
+
this.values.set(frame);
|
|
644
|
+
this.velocities.fill(0);
|
|
645
|
+
this._hasTarget = true;
|
|
646
|
+
}
|
|
634
647
|
/**
|
|
635
648
|
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
636
649
|
*
|
|
@@ -686,7 +699,8 @@ var BlendshapeSmoother = class {
|
|
|
686
699
|
var logger4 = createLogger("A2EProcessor");
|
|
687
700
|
var FRAME_RATE = 30;
|
|
688
701
|
var DRIP_INTERVAL_MS = 33;
|
|
689
|
-
var
|
|
702
|
+
var HOLD_DURATION_MS = 400;
|
|
703
|
+
var GAP_DECAY_HALFLIFE_S = 0.08;
|
|
690
704
|
var _A2EProcessor = class _A2EProcessor {
|
|
691
705
|
constructor(config) {
|
|
692
706
|
this.writeOffset = 0;
|
|
@@ -697,10 +711,12 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
697
711
|
// Push mode state
|
|
698
712
|
this._latestFrame = null;
|
|
699
713
|
this.dripInterval = null;
|
|
714
|
+
// Last-frame-hold for pull mode (prevents avatar freezing between frames)
|
|
715
|
+
this.lastPulledFrame = null;
|
|
700
716
|
this.lastDequeuedTime = 0;
|
|
701
|
-
this.
|
|
702
|
-
this.
|
|
703
|
-
this.
|
|
717
|
+
this.decayBuffer = null;
|
|
718
|
+
this.gapDecayStarted = false;
|
|
719
|
+
this.lastSmootherUpdate = 0;
|
|
704
720
|
// Inference serialization
|
|
705
721
|
this.inferenceRunning = false;
|
|
706
722
|
this.pendingChunks = [];
|
|
@@ -713,9 +729,9 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
713
729
|
this.identityIndex = config.identityIndex ?? 0;
|
|
714
730
|
this.onFrame = config.onFrame;
|
|
715
731
|
this.onError = config.onError;
|
|
716
|
-
this.smoother = new BlendshapeSmoother({ halflife: config.smoothingHalflife ?? 0.06 });
|
|
717
732
|
this.bufferCapacity = this.chunkSize * 2;
|
|
718
733
|
this.buffer = new Float32Array(this.bufferCapacity);
|
|
734
|
+
this.smoother = new BlendshapeSmoother({ halflife: GAP_DECAY_HALFLIFE_S });
|
|
719
735
|
}
|
|
720
736
|
// ═══════════════════════════════════════════════════════════════════════
|
|
721
737
|
// Audio Input
|
|
@@ -813,14 +829,14 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
813
829
|
this.timestampedQueue = [];
|
|
814
830
|
this.plainQueue = [];
|
|
815
831
|
this._latestFrame = null;
|
|
816
|
-
this.
|
|
832
|
+
this.lastPulledFrame = null;
|
|
817
833
|
this.lastDequeuedTime = 0;
|
|
818
|
-
this.lastUpdateTime = 0;
|
|
819
|
-
this.neutralTriggered = false;
|
|
820
|
-
this.outputBuffer = null;
|
|
821
834
|
this.pendingChunks = [];
|
|
822
835
|
this.inferenceRunning = false;
|
|
823
836
|
this.getFrameCallCount = 0;
|
|
837
|
+
this.smoother.reset();
|
|
838
|
+
this.gapDecayStarted = false;
|
|
839
|
+
this.lastSmootherUpdate = 0;
|
|
824
840
|
}
|
|
825
841
|
// ═══════════════════════════════════════════════════════════════════════
|
|
826
842
|
// Frame Output — Pull Mode (TTS playback)
|
|
@@ -836,7 +852,6 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
836
852
|
*/
|
|
837
853
|
getFrameForTime(currentTime) {
|
|
838
854
|
this.getFrameCallCount++;
|
|
839
|
-
const now = getClock().now();
|
|
840
855
|
const discardWindow = this.backend.backend === "wasm" ? 1.5 : 0.5;
|
|
841
856
|
let discardCount = 0;
|
|
842
857
|
while (this.timestampedQueue.length > 0 && this.timestampedQueue[0].timestamp < currentTime - discardWindow) {
|
|
@@ -852,20 +867,14 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
852
867
|
nextFrameTs: this.timestampedQueue.length > 0 ? this.timestampedQueue[0].timestamp.toFixed(3) : "none"
|
|
853
868
|
});
|
|
854
869
|
}
|
|
855
|
-
let newDequeue = false;
|
|
856
870
|
if (this.timestampedQueue.length > 0 && this.timestampedQueue[0].timestamp <= currentTime) {
|
|
857
871
|
const { frame } = this.timestampedQueue.shift();
|
|
858
|
-
|
|
859
|
-
this.
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
this.lastUpdateTime = now;
|
|
863
|
-
}
|
|
864
|
-
newDequeue = true;
|
|
865
|
-
this.neutralTriggered = false;
|
|
866
|
-
this.lastDequeuedTime = now;
|
|
872
|
+
this.lastPulledFrame = frame;
|
|
873
|
+
this.lastDequeuedTime = getClock().now();
|
|
874
|
+
this.gapDecayStarted = false;
|
|
875
|
+
return frame;
|
|
867
876
|
}
|
|
868
|
-
if (
|
|
877
|
+
if (this.timestampedQueue.length > 0 && this.getFrameCallCount % 60 === 0) {
|
|
869
878
|
logger4.debug("getFrameForTime: waiting for playback time to reach queued frames", {
|
|
870
879
|
queueLen: this.timestampedQueue.length,
|
|
871
880
|
frontTimestamp: this.timestampedQueue[0].timestamp.toFixed(4),
|
|
@@ -873,24 +882,34 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
873
882
|
delta: (this.timestampedQueue[0].timestamp - currentTime).toFixed(4)
|
|
874
883
|
});
|
|
875
884
|
}
|
|
876
|
-
if (
|
|
885
|
+
if (this.lastPulledFrame) {
|
|
886
|
+
const now = getClock().now();
|
|
877
887
|
const elapsed = now - this.lastDequeuedTime;
|
|
878
|
-
if (elapsed
|
|
888
|
+
if (elapsed < HOLD_DURATION_MS) {
|
|
889
|
+
return this.lastPulledFrame;
|
|
890
|
+
}
|
|
891
|
+
if (!this.gapDecayStarted) {
|
|
892
|
+
this.smoother.setPosition(this.lastPulledFrame);
|
|
879
893
|
this.smoother.decayToNeutral();
|
|
880
|
-
this.
|
|
894
|
+
this.gapDecayStarted = true;
|
|
895
|
+
this.lastSmootherUpdate = now;
|
|
881
896
|
}
|
|
897
|
+
const dt = Math.min((now - this.lastSmootherUpdate) / 1e3, 0.1);
|
|
898
|
+
this.lastSmootherUpdate = now;
|
|
899
|
+
const smoothed = this.smoother.update(dt);
|
|
900
|
+
let maxVal = 0;
|
|
901
|
+
for (let i = 0; i < 52; i++) {
|
|
902
|
+
if (smoothed[i] > maxVal) maxVal = smoothed[i];
|
|
903
|
+
}
|
|
904
|
+
if (maxVal < 1e-3) {
|
|
905
|
+
this.lastPulledFrame = null;
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
908
|
+
if (!this.decayBuffer) this.decayBuffer = new Float32Array(52);
|
|
909
|
+
this.decayBuffer.set(smoothed);
|
|
910
|
+
return this.decayBuffer;
|
|
882
911
|
}
|
|
883
|
-
|
|
884
|
-
return null;
|
|
885
|
-
}
|
|
886
|
-
const dt = Math.min((now - this.lastUpdateTime) / 1e3, 0.1);
|
|
887
|
-
const smoothed = this.smoother.update(dt);
|
|
888
|
-
this.lastUpdateTime = now;
|
|
889
|
-
if (newDequeue || !this.outputBuffer) {
|
|
890
|
-
this.outputBuffer = new Float32Array(52);
|
|
891
|
-
}
|
|
892
|
-
this.outputBuffer.set(smoothed);
|
|
893
|
-
return this.outputBuffer;
|
|
912
|
+
return null;
|
|
894
913
|
}
|
|
895
914
|
// ═══════════════════════════════════════════════════════════════════════
|
|
896
915
|
// Frame Output — Push Mode (live mic, game loop)
|