@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.mjs
CHANGED
|
@@ -608,12 +608,99 @@ var AudioChunkCoalescer = class {
|
|
|
608
608
|
}
|
|
609
609
|
};
|
|
610
610
|
|
|
611
|
+
// src/inference/BlendshapeSmoother.ts
|
|
612
|
+
var NUM_BLENDSHAPES = 52;
|
|
613
|
+
var BlendshapeSmoother = class {
|
|
614
|
+
constructor(config) {
|
|
615
|
+
/** Whether any target has been set */
|
|
616
|
+
this._hasTarget = false;
|
|
617
|
+
this.halflife = config?.halflife ?? 0.06;
|
|
618
|
+
this.values = new Float32Array(NUM_BLENDSHAPES);
|
|
619
|
+
this.velocities = new Float32Array(NUM_BLENDSHAPES);
|
|
620
|
+
this.targets = new Float32Array(NUM_BLENDSHAPES);
|
|
621
|
+
}
|
|
622
|
+
/** Whether a target frame has been set (false until first setTarget call) */
|
|
623
|
+
get hasTarget() {
|
|
624
|
+
return this._hasTarget;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Set new target frame from inference output.
|
|
628
|
+
* Springs will converge toward these values on subsequent update() calls.
|
|
629
|
+
*/
|
|
630
|
+
setTarget(frame) {
|
|
631
|
+
this.targets.set(frame);
|
|
632
|
+
this._hasTarget = true;
|
|
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
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
649
|
+
*
|
|
650
|
+
* Call this every render frame (e.g., inside requestAnimationFrame).
|
|
651
|
+
* Returns the internal values buffer — do NOT mutate the returned array.
|
|
652
|
+
*
|
|
653
|
+
* @param dt - Time step in seconds (e.g., 1/60 for 60fps)
|
|
654
|
+
* @returns Smoothed blendshape values (Float32Array of 52)
|
|
655
|
+
*/
|
|
656
|
+
update(dt) {
|
|
657
|
+
if (!this._hasTarget) {
|
|
658
|
+
return this.values;
|
|
659
|
+
}
|
|
660
|
+
if (this.halflife <= 0) {
|
|
661
|
+
this.values.set(this.targets);
|
|
662
|
+
this.velocities.fill(0);
|
|
663
|
+
return this.values;
|
|
664
|
+
}
|
|
665
|
+
const damping = Math.LN2 / this.halflife;
|
|
666
|
+
const eydt = Math.exp(-damping * dt);
|
|
667
|
+
for (let i = 0; i < NUM_BLENDSHAPES; i++) {
|
|
668
|
+
const j0 = this.values[i] - this.targets[i];
|
|
669
|
+
const j1 = this.velocities[i] + j0 * damping;
|
|
670
|
+
this.values[i] = eydt * (j0 + j1 * dt) + this.targets[i];
|
|
671
|
+
this.velocities[i] = eydt * (this.velocities[i] - j1 * damping * dt);
|
|
672
|
+
this.values[i] = Math.max(0, Math.min(1, this.values[i]));
|
|
673
|
+
}
|
|
674
|
+
return this.values;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Decay all spring targets to neutral (0).
|
|
678
|
+
*
|
|
679
|
+
* Call when inference stalls (no new frames for threshold duration).
|
|
680
|
+
* The springs will smoothly close the mouth / relax the face over
|
|
681
|
+
* the halflife period rather than freezing.
|
|
682
|
+
*/
|
|
683
|
+
decayToNeutral() {
|
|
684
|
+
this.targets.fill(0);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Reset all state (values, velocities, targets).
|
|
688
|
+
* Call when starting a new playback session.
|
|
689
|
+
*/
|
|
690
|
+
reset() {
|
|
691
|
+
this.values.fill(0);
|
|
692
|
+
this.velocities.fill(0);
|
|
693
|
+
this.targets.fill(0);
|
|
694
|
+
this._hasTarget = false;
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
|
|
611
698
|
// src/inference/A2EProcessor.ts
|
|
612
699
|
var logger4 = createLogger("A2EProcessor");
|
|
613
700
|
var FRAME_RATE = 30;
|
|
614
701
|
var DRIP_INTERVAL_MS = 33;
|
|
615
702
|
var HOLD_DURATION_MS = 400;
|
|
616
|
-
var
|
|
703
|
+
var GAP_DECAY_HALFLIFE_S = 0.08;
|
|
617
704
|
var _A2EProcessor = class _A2EProcessor {
|
|
618
705
|
constructor(config) {
|
|
619
706
|
this.writeOffset = 0;
|
|
@@ -628,6 +715,8 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
628
715
|
this.lastPulledFrame = null;
|
|
629
716
|
this.lastDequeuedTime = 0;
|
|
630
717
|
this.decayBuffer = null;
|
|
718
|
+
this.gapDecayStarted = false;
|
|
719
|
+
this.lastSmootherUpdate = 0;
|
|
631
720
|
// Inference serialization
|
|
632
721
|
this.inferenceRunning = false;
|
|
633
722
|
this.pendingChunks = [];
|
|
@@ -642,6 +731,7 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
642
731
|
this.onError = config.onError;
|
|
643
732
|
this.bufferCapacity = this.chunkSize * 2;
|
|
644
733
|
this.buffer = new Float32Array(this.bufferCapacity);
|
|
734
|
+
this.smoother = new BlendshapeSmoother({ halflife: GAP_DECAY_HALFLIFE_S });
|
|
645
735
|
}
|
|
646
736
|
// ═══════════════════════════════════════════════════════════════════════
|
|
647
737
|
// Audio Input
|
|
@@ -744,6 +834,9 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
744
834
|
this.pendingChunks = [];
|
|
745
835
|
this.inferenceRunning = false;
|
|
746
836
|
this.getFrameCallCount = 0;
|
|
837
|
+
this.smoother.reset();
|
|
838
|
+
this.gapDecayStarted = false;
|
|
839
|
+
this.lastSmootherUpdate = 0;
|
|
747
840
|
}
|
|
748
841
|
// ═══════════════════════════════════════════════════════════════════════
|
|
749
842
|
// Frame Output — Pull Mode (TTS playback)
|
|
@@ -778,6 +871,7 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
778
871
|
const { frame } = this.timestampedQueue.shift();
|
|
779
872
|
this.lastPulledFrame = frame;
|
|
780
873
|
this.lastDequeuedTime = getClock().now();
|
|
874
|
+
this.gapDecayStarted = false;
|
|
781
875
|
return frame;
|
|
782
876
|
}
|
|
783
877
|
if (this.timestampedQueue.length > 0 && this.getFrameCallCount % 60 === 0) {
|
|
@@ -789,21 +883,30 @@ var _A2EProcessor = class _A2EProcessor {
|
|
|
789
883
|
});
|
|
790
884
|
}
|
|
791
885
|
if (this.lastPulledFrame) {
|
|
792
|
-
const
|
|
886
|
+
const now = getClock().now();
|
|
887
|
+
const elapsed = now - this.lastDequeuedTime;
|
|
793
888
|
if (elapsed < HOLD_DURATION_MS) {
|
|
794
889
|
return this.lastPulledFrame;
|
|
795
890
|
}
|
|
796
|
-
|
|
797
|
-
|
|
891
|
+
if (!this.gapDecayStarted) {
|
|
892
|
+
this.smoother.setPosition(this.lastPulledFrame);
|
|
893
|
+
this.smoother.decayToNeutral();
|
|
894
|
+
this.gapDecayStarted = true;
|
|
895
|
+
this.lastSmootherUpdate = now;
|
|
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) {
|
|
798
905
|
this.lastPulledFrame = null;
|
|
799
906
|
return null;
|
|
800
907
|
}
|
|
801
|
-
const t = decayElapsed / DECAY_DURATION_MS;
|
|
802
|
-
const factor = (1 - t) * (1 - t);
|
|
803
908
|
if (!this.decayBuffer) this.decayBuffer = new Float32Array(52);
|
|
804
|
-
|
|
805
|
-
this.decayBuffer[i] = this.lastPulledFrame[i] * factor;
|
|
806
|
-
}
|
|
909
|
+
this.decayBuffer.set(smoothed);
|
|
807
910
|
return this.decayBuffer;
|
|
808
911
|
}
|
|
809
912
|
return null;
|
|
@@ -10137,80 +10240,6 @@ var InterruptionHandler = class extends EventEmitter {
|
|
|
10137
10240
|
}
|
|
10138
10241
|
};
|
|
10139
10242
|
|
|
10140
|
-
// src/inference/BlendshapeSmoother.ts
|
|
10141
|
-
var NUM_BLENDSHAPES = 52;
|
|
10142
|
-
var BlendshapeSmoother = class {
|
|
10143
|
-
constructor(config) {
|
|
10144
|
-
/** Whether any target has been set */
|
|
10145
|
-
this._hasTarget = false;
|
|
10146
|
-
this.halflife = config?.halflife ?? 0.06;
|
|
10147
|
-
this.values = new Float32Array(NUM_BLENDSHAPES);
|
|
10148
|
-
this.velocities = new Float32Array(NUM_BLENDSHAPES);
|
|
10149
|
-
this.targets = new Float32Array(NUM_BLENDSHAPES);
|
|
10150
|
-
}
|
|
10151
|
-
/** Whether a target frame has been set (false until first setTarget call) */
|
|
10152
|
-
get hasTarget() {
|
|
10153
|
-
return this._hasTarget;
|
|
10154
|
-
}
|
|
10155
|
-
/**
|
|
10156
|
-
* Set new target frame from inference output.
|
|
10157
|
-
* Springs will converge toward these values on subsequent update() calls.
|
|
10158
|
-
*/
|
|
10159
|
-
setTarget(frame) {
|
|
10160
|
-
this.targets.set(frame);
|
|
10161
|
-
this._hasTarget = true;
|
|
10162
|
-
}
|
|
10163
|
-
/**
|
|
10164
|
-
* Advance all 52 springs by `dt` seconds and return the smoothed frame.
|
|
10165
|
-
*
|
|
10166
|
-
* Call this every render frame (e.g., inside requestAnimationFrame).
|
|
10167
|
-
* Returns the internal values buffer — do NOT mutate the returned array.
|
|
10168
|
-
*
|
|
10169
|
-
* @param dt - Time step in seconds (e.g., 1/60 for 60fps)
|
|
10170
|
-
* @returns Smoothed blendshape values (Float32Array of 52)
|
|
10171
|
-
*/
|
|
10172
|
-
update(dt) {
|
|
10173
|
-
if (!this._hasTarget) {
|
|
10174
|
-
return this.values;
|
|
10175
|
-
}
|
|
10176
|
-
if (this.halflife <= 0) {
|
|
10177
|
-
this.values.set(this.targets);
|
|
10178
|
-
this.velocities.fill(0);
|
|
10179
|
-
return this.values;
|
|
10180
|
-
}
|
|
10181
|
-
const damping = Math.LN2 / this.halflife;
|
|
10182
|
-
const eydt = Math.exp(-damping * dt);
|
|
10183
|
-
for (let i = 0; i < NUM_BLENDSHAPES; i++) {
|
|
10184
|
-
const j0 = this.values[i] - this.targets[i];
|
|
10185
|
-
const j1 = this.velocities[i] + j0 * damping;
|
|
10186
|
-
this.values[i] = eydt * (j0 + j1 * dt) + this.targets[i];
|
|
10187
|
-
this.velocities[i] = eydt * (this.velocities[i] - j1 * damping * dt);
|
|
10188
|
-
this.values[i] = Math.max(0, Math.min(1, this.values[i]));
|
|
10189
|
-
}
|
|
10190
|
-
return this.values;
|
|
10191
|
-
}
|
|
10192
|
-
/**
|
|
10193
|
-
* Decay all spring targets to neutral (0).
|
|
10194
|
-
*
|
|
10195
|
-
* Call when inference stalls (no new frames for threshold duration).
|
|
10196
|
-
* The springs will smoothly close the mouth / relax the face over
|
|
10197
|
-
* the halflife period rather than freezing.
|
|
10198
|
-
*/
|
|
10199
|
-
decayToNeutral() {
|
|
10200
|
-
this.targets.fill(0);
|
|
10201
|
-
}
|
|
10202
|
-
/**
|
|
10203
|
-
* Reset all state (values, velocities, targets).
|
|
10204
|
-
* Call when starting a new playback session.
|
|
10205
|
-
*/
|
|
10206
|
-
reset() {
|
|
10207
|
-
this.values.fill(0);
|
|
10208
|
-
this.velocities.fill(0);
|
|
10209
|
-
this.targets.fill(0);
|
|
10210
|
-
this._hasTarget = false;
|
|
10211
|
-
}
|
|
10212
|
-
};
|
|
10213
|
-
|
|
10214
10243
|
// src/inference/SafariSpeechRecognition.ts
|
|
10215
10244
|
var logger33 = createLogger("SafariSpeech");
|
|
10216
10245
|
var SafariSpeechRecognition = class _SafariSpeechRecognition {
|