@omote/three 0.2.0 → 0.3.1
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 +25 -0
- package/dist/index.cjs +217 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -38
- package/dist/index.d.ts +85 -38
- package/dist/index.js +220 -39
- package/dist/index.js.map +1 -1
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -70,6 +70,31 @@ Traverse a Three.js scene to find bones and morph targets. Returns pre-computed
|
|
|
70
70
|
|
|
71
71
|
Write 52 ARKit blendshape weights to morph targets using pre-computed indices.
|
|
72
72
|
|
|
73
|
+
### Voice Integration
|
|
74
|
+
|
|
75
|
+
`connectVoice()` combines speaker + listener + interruption handling:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { OmoteAvatar } from '@omote/three';
|
|
79
|
+
import { KokoroTTSInference } from '@omote/core';
|
|
80
|
+
|
|
81
|
+
const avatar = new OmoteAvatar({ target: gltf.scene });
|
|
82
|
+
await avatar.connectVoice({
|
|
83
|
+
tts: new KokoroTTSInference({ defaultVoice: 'af_heart' }),
|
|
84
|
+
interruptionEnabled: true,
|
|
85
|
+
onTranscript: async (text) => {
|
|
86
|
+
const res = await fetch('/api/chat', { body: text });
|
|
87
|
+
return await res.text();
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Or use individual APIs:
|
|
92
|
+
await avatar.connectSpeaker(ttsBackend, { lam: createA2E() });
|
|
93
|
+
await avatar.speak("Hello!");
|
|
94
|
+
await avatar.connectListener({ models: { senseVoice: {...}, vad: {...} } });
|
|
95
|
+
await avatar.startListening();
|
|
96
|
+
```
|
|
97
|
+
|
|
73
98
|
### `BlendshapeController` (low-level)
|
|
74
99
|
|
|
75
100
|
Direct morph target weight control for custom integrations.
|
package/dist/index.cjs
CHANGED
|
@@ -21,7 +21,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
BlendshapeController: () => BlendshapeController,
|
|
24
|
-
OmoteA2E: () => OmoteA2E,
|
|
25
24
|
OmoteAvatar: () => OmoteAvatar,
|
|
26
25
|
discoverScene: () => discoverScene,
|
|
27
26
|
writeBlendshapes: () => writeBlendshapes
|
|
@@ -133,6 +132,12 @@ var OmoteAvatar = class {
|
|
|
133
132
|
constructor(options) {
|
|
134
133
|
this.frameSourceCallback = null;
|
|
135
134
|
this.connectedSource = null;
|
|
135
|
+
// TTS integration
|
|
136
|
+
this.ttsSpeaker = null;
|
|
137
|
+
// Speech listener
|
|
138
|
+
this.speechListener = null;
|
|
139
|
+
// Voice orchestrator
|
|
140
|
+
this.voiceOrchestrator = null;
|
|
136
141
|
// State
|
|
137
142
|
this.currentBlendshapes = null;
|
|
138
143
|
this._emotion = null;
|
|
@@ -212,9 +217,15 @@ var OmoteAvatar = class {
|
|
|
212
217
|
* disconnects the previous one.
|
|
213
218
|
*/
|
|
214
219
|
connectFrameSource(source) {
|
|
220
|
+
if (this.ttsSpeaker && source !== this.ttsSpeaker.frameSource) {
|
|
221
|
+
this.ttsSpeaker.stop();
|
|
222
|
+
}
|
|
215
223
|
this.disconnectFrameSource();
|
|
216
224
|
this.frameSourceCallback = (frame) => {
|
|
217
225
|
this.currentBlendshapes = frame.blendshapes;
|
|
226
|
+
if (frame.emotion !== void 0) {
|
|
227
|
+
this._emotion = frame.emotion;
|
|
228
|
+
}
|
|
218
229
|
};
|
|
219
230
|
source.on("frame", this.frameSourceCallback);
|
|
220
231
|
this.connectedSource = source;
|
|
@@ -232,6 +243,182 @@ var OmoteAvatar = class {
|
|
|
232
243
|
this.frameSourceCallback = null;
|
|
233
244
|
}
|
|
234
245
|
// -------------------------------------------------------------------------
|
|
246
|
+
// Speaker (TTS → lip sync)
|
|
247
|
+
// -------------------------------------------------------------------------
|
|
248
|
+
/**
|
|
249
|
+
* Connect a TTS backend for speak() / streamText() support.
|
|
250
|
+
* Loads LAM model and creates internal PlaybackPipeline.
|
|
251
|
+
*
|
|
252
|
+
* @param tts - TTS backend (e.g., KokoroTTSInference, ElevenLabs adapter)
|
|
253
|
+
* @param config - A2E, expression profile, and playback configuration
|
|
254
|
+
*/
|
|
255
|
+
async connectSpeaker(tts, config) {
|
|
256
|
+
await this.disconnectSpeaker();
|
|
257
|
+
this.ttsSpeaker = new import_core2.TTSSpeaker();
|
|
258
|
+
await this.ttsSpeaker.connect(tts, config);
|
|
259
|
+
this.connectFrameSource(this.ttsSpeaker.frameSource);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Synthesize text and play with lip sync.
|
|
263
|
+
* Auto-aborts previous speak if still in progress.
|
|
264
|
+
*
|
|
265
|
+
* @param text - Text to synthesize
|
|
266
|
+
* @param options - Optional voice override and abort signal
|
|
267
|
+
*/
|
|
268
|
+
async speak(text, options) {
|
|
269
|
+
if (this.voiceOrchestrator) {
|
|
270
|
+
await this.voiceOrchestrator.speak(text, options);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (!this.ttsSpeaker) {
|
|
274
|
+
throw new Error("No speaker connected. Call connectSpeaker() first.");
|
|
275
|
+
}
|
|
276
|
+
this._isSpeaking = true;
|
|
277
|
+
this._state = "speaking";
|
|
278
|
+
try {
|
|
279
|
+
await this.ttsSpeaker.speak(text, options);
|
|
280
|
+
} finally {
|
|
281
|
+
this._isSpeaking = false;
|
|
282
|
+
if (this._state === "speaking") {
|
|
283
|
+
this._state = "idle";
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Stream LLM tokens with sentence-buffered TTS + lip sync.
|
|
289
|
+
* Returns a sink: call push(token) for each token, end() when done.
|
|
290
|
+
*/
|
|
291
|
+
async streamText(options) {
|
|
292
|
+
if (this.voiceOrchestrator) {
|
|
293
|
+
return this.voiceOrchestrator.streamText(options);
|
|
294
|
+
}
|
|
295
|
+
if (!this.ttsSpeaker) {
|
|
296
|
+
throw new Error("No speaker connected. Call connectSpeaker() first.");
|
|
297
|
+
}
|
|
298
|
+
this._isSpeaking = true;
|
|
299
|
+
this._state = "speaking";
|
|
300
|
+
const stream = await this.ttsSpeaker.streamText(options ?? {});
|
|
301
|
+
return {
|
|
302
|
+
push: stream.push,
|
|
303
|
+
end: async () => {
|
|
304
|
+
try {
|
|
305
|
+
await stream.end();
|
|
306
|
+
} finally {
|
|
307
|
+
this._isSpeaking = false;
|
|
308
|
+
if (this._state === "speaking") this._state = "idle";
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/** Stop current TTS playback. */
|
|
314
|
+
stopSpeaking() {
|
|
315
|
+
if (this.voiceOrchestrator) {
|
|
316
|
+
this.voiceOrchestrator.stopSpeaking();
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.ttsSpeaker?.stop();
|
|
320
|
+
}
|
|
321
|
+
/** Disconnect speaker and dispose its resources. */
|
|
322
|
+
async disconnectSpeaker() {
|
|
323
|
+
if (this.ttsSpeaker) {
|
|
324
|
+
this.disconnectFrameSource();
|
|
325
|
+
await this.ttsSpeaker.dispose();
|
|
326
|
+
this.ttsSpeaker = null;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/** @deprecated Use connectSpeaker(). Will be removed in v1.0. */
|
|
330
|
+
async connectTTS(tts, config) {
|
|
331
|
+
return this.connectSpeaker(tts, config);
|
|
332
|
+
}
|
|
333
|
+
/** @deprecated Use disconnectSpeaker(). Will be removed in v1.0. */
|
|
334
|
+
async disconnectTTS() {
|
|
335
|
+
return this.disconnectSpeaker();
|
|
336
|
+
}
|
|
337
|
+
// -------------------------------------------------------------------------
|
|
338
|
+
// Listener (mic → VAD → ASR → transcript)
|
|
339
|
+
// -------------------------------------------------------------------------
|
|
340
|
+
/**
|
|
341
|
+
* Connect a speech listener for startListening() / onTranscript() support.
|
|
342
|
+
* Loads ASR + VAD models.
|
|
343
|
+
*/
|
|
344
|
+
async connectListener(config) {
|
|
345
|
+
await this.disconnectListener();
|
|
346
|
+
this.speechListener = new import_core2.SpeechListener(config);
|
|
347
|
+
await this.speechListener.loadModels();
|
|
348
|
+
}
|
|
349
|
+
/** Start listening for user speech. Requires connectListener() or connectVoice() first. */
|
|
350
|
+
async startListening() {
|
|
351
|
+
if (this.voiceOrchestrator) {
|
|
352
|
+
await this.voiceOrchestrator.startListening();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (!this.speechListener) {
|
|
356
|
+
throw new Error("No listener connected. Call connectListener() first.");
|
|
357
|
+
}
|
|
358
|
+
this._state = "listening";
|
|
359
|
+
await this.speechListener.start();
|
|
360
|
+
}
|
|
361
|
+
/** Stop listening. */
|
|
362
|
+
stopListening() {
|
|
363
|
+
if (this.voiceOrchestrator) {
|
|
364
|
+
this.voiceOrchestrator.stopListening();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
this.speechListener?.stop();
|
|
368
|
+
if (this._state === "listening") this._state = "idle";
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Subscribe to transcript events. Returns an unsubscribe function.
|
|
372
|
+
* Requires connectListener() first.
|
|
373
|
+
*/
|
|
374
|
+
onTranscript(callback) {
|
|
375
|
+
const listener = this.speechListener ?? this.voiceOrchestrator?.listener;
|
|
376
|
+
if (!listener) {
|
|
377
|
+
throw new Error("No listener connected. Call connectListener() or connectVoice() first.");
|
|
378
|
+
}
|
|
379
|
+
listener.on("transcript", callback);
|
|
380
|
+
return () => {
|
|
381
|
+
listener.off?.("transcript", callback);
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
/** Disconnect listener and dispose its resources. */
|
|
385
|
+
async disconnectListener() {
|
|
386
|
+
if (this.speechListener) {
|
|
387
|
+
await this.speechListener.dispose();
|
|
388
|
+
this.speechListener = null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// -------------------------------------------------------------------------
|
|
392
|
+
// Voice (combined speaker + listener + interruption)
|
|
393
|
+
// -------------------------------------------------------------------------
|
|
394
|
+
/**
|
|
395
|
+
* Connect voice with automatic speaker + listener + interruption wiring.
|
|
396
|
+
* Supports both local TTS (mode: 'local') and cloud TTS (mode: 'cloud').
|
|
397
|
+
* Does NOT auto-start listening — call startListening() when ready.
|
|
398
|
+
*
|
|
399
|
+
* Backward compatible: `mode` defaults to 'local' when not specified.
|
|
400
|
+
*/
|
|
401
|
+
async connectVoice(config) {
|
|
402
|
+
await this.disconnectVoice();
|
|
403
|
+
this.voiceOrchestrator = new import_core2.VoiceOrchestrator();
|
|
404
|
+
await this.voiceOrchestrator.connect(config);
|
|
405
|
+
if (this.voiceOrchestrator.frameSource) {
|
|
406
|
+
this.connectFrameSource(this.voiceOrchestrator.frameSource);
|
|
407
|
+
}
|
|
408
|
+
this.voiceOrchestrator.on("state", (state) => {
|
|
409
|
+
this._state = state;
|
|
410
|
+
this._isSpeaking = state === "speaking";
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/** Disconnect voice (speaker + listener + interruption). */
|
|
414
|
+
async disconnectVoice() {
|
|
415
|
+
if (this.voiceOrchestrator) {
|
|
416
|
+
this.disconnectFrameSource();
|
|
417
|
+
await this.voiceOrchestrator.disconnect();
|
|
418
|
+
this.voiceOrchestrator = null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// -------------------------------------------------------------------------
|
|
235
422
|
// State setters
|
|
236
423
|
// -------------------------------------------------------------------------
|
|
237
424
|
/** Set raw blendshapes directly (alternative to connectFrameSource). */
|
|
@@ -254,6 +441,10 @@ var OmoteAvatar = class {
|
|
|
254
441
|
setAudioEnergy(energy) {
|
|
255
442
|
this._audioEnergy = energy;
|
|
256
443
|
}
|
|
444
|
+
/** Update character expression profile at runtime. */
|
|
445
|
+
setProfile(profile) {
|
|
446
|
+
this.controller.setProfile(profile);
|
|
447
|
+
}
|
|
257
448
|
// -------------------------------------------------------------------------
|
|
258
449
|
// Accessors
|
|
259
450
|
// -------------------------------------------------------------------------
|
|
@@ -273,6 +464,26 @@ var OmoteAvatar = class {
|
|
|
273
464
|
get mappedBlendshapeCount() {
|
|
274
465
|
return this.discovery.mappedBlendshapeCount;
|
|
275
466
|
}
|
|
467
|
+
/** Whether the avatar is currently speaking via TTS. */
|
|
468
|
+
get isSpeaking() {
|
|
469
|
+
return this._isSpeaking;
|
|
470
|
+
}
|
|
471
|
+
/** Whether the avatar is currently listening for speech. */
|
|
472
|
+
get isListening() {
|
|
473
|
+
return this._state === "listening";
|
|
474
|
+
}
|
|
475
|
+
/** Current conversational state. */
|
|
476
|
+
get conversationalState() {
|
|
477
|
+
return this._state;
|
|
478
|
+
}
|
|
479
|
+
/** Access the internal TTSSpeaker (null if not connected). */
|
|
480
|
+
get speaker() {
|
|
481
|
+
return this.ttsSpeaker ?? this.voiceOrchestrator?.speaker ?? null;
|
|
482
|
+
}
|
|
483
|
+
/** Access the internal SpeechListener (null if not connected). */
|
|
484
|
+
get listener() {
|
|
485
|
+
return this.speechListener ?? this.voiceOrchestrator?.listener ?? null;
|
|
486
|
+
}
|
|
276
487
|
// -------------------------------------------------------------------------
|
|
277
488
|
// Lifecycle
|
|
278
489
|
// -------------------------------------------------------------------------
|
|
@@ -285,8 +496,11 @@ var OmoteAvatar = class {
|
|
|
285
496
|
this._audioEnergy = 0;
|
|
286
497
|
this.controller.reset();
|
|
287
498
|
}
|
|
288
|
-
/** Disconnect frame sources and dispose the controller. */
|
|
289
|
-
dispose() {
|
|
499
|
+
/** Disconnect all voice resources, frame sources, and dispose the controller. */
|
|
500
|
+
async dispose() {
|
|
501
|
+
await this.disconnectVoice();
|
|
502
|
+
await this.disconnectSpeaker();
|
|
503
|
+
await this.disconnectListener();
|
|
290
504
|
this.disconnectFrameSource();
|
|
291
505
|
this.controller.dispose();
|
|
292
506
|
logger2.debug("Disposed");
|
|
@@ -354,40 +568,4 @@ var BlendshapeController = class {
|
|
|
354
568
|
this.currentWeights = [];
|
|
355
569
|
}
|
|
356
570
|
};
|
|
357
|
-
|
|
358
|
-
// src/OmoteA2E.ts
|
|
359
|
-
var import_core4 = require("@omote/core");
|
|
360
|
-
var OmoteA2E = class {
|
|
361
|
-
constructor(options) {
|
|
362
|
-
const { target, controllerOptions, ...orchestratorConfig } = options;
|
|
363
|
-
this.controller = new BlendshapeController(target, controllerOptions);
|
|
364
|
-
this.orchestrator = new import_core4.A2EOrchestrator(orchestratorConfig);
|
|
365
|
-
}
|
|
366
|
-
async load() {
|
|
367
|
-
return this.orchestrator.load();
|
|
368
|
-
}
|
|
369
|
-
async start() {
|
|
370
|
-
return this.orchestrator.start();
|
|
371
|
-
}
|
|
372
|
-
stop() {
|
|
373
|
-
this.orchestrator.stop();
|
|
374
|
-
}
|
|
375
|
-
update() {
|
|
376
|
-
const w = this.orchestrator.latestWeights;
|
|
377
|
-
if (w) this.controller.update(w);
|
|
378
|
-
}
|
|
379
|
-
async dispose() {
|
|
380
|
-
await this.orchestrator.dispose();
|
|
381
|
-
this.controller.dispose();
|
|
382
|
-
}
|
|
383
|
-
get isReady() {
|
|
384
|
-
return this.orchestrator.isReady;
|
|
385
|
-
}
|
|
386
|
-
get isStreaming() {
|
|
387
|
-
return this.orchestrator.isStreaming;
|
|
388
|
-
}
|
|
389
|
-
get backend() {
|
|
390
|
-
return this.orchestrator.backend;
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
571
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/OmoteAvatar.ts","../src/SceneDiscovery.ts","../src/BlendshapeWriter.ts","../src/BlendshapeController.ts","../src/OmoteA2E.ts"],"sourcesContent":["// High-level\nexport { OmoteAvatar } from './OmoteAvatar';\nexport type { OmoteAvatarOptions, FrameSource } from './OmoteAvatar';\n\n// Scene utilities (canonical — r3f imports from here)\nexport { discoverScene } from './SceneDiscovery';\nexport type { SceneDiscoveryResult, MorphIndexEntry, SceneObject, DiscoveredMesh, DiscoveredBone } from './SceneDiscovery';\nexport { writeBlendshapes } from './BlendshapeWriter';\n\n// Low-level escape hatch\nexport { BlendshapeController } from './BlendshapeController';\nexport type { BlendshapeControllerOptions } from './BlendshapeController';\n\n/** @deprecated Use OmoteAvatar instead */\nexport { OmoteA2E } from './OmoteA2E';\nexport type { OmoteA2EOptions } from './OmoteA2E';\n","/**\n * OmoteAvatar — Full-featured Three.js avatar class.\n *\n * Wraps CharacterController from @omote/core with Three.js scene discovery,\n * blendshape writing, and gaze bone rotation. Drop-in avatar for any\n * Three.js app that has a render loop.\n *\n * @example\n * ```ts\n * import { OmoteAvatar } from '@omote/three';\n * import { createA2E, PlaybackPipeline } from '@omote/core';\n *\n * const avatar = new OmoteAvatar({ target: avatarModel });\n * const lam = createA2E();\n * await lam.load();\n * const pipeline = new PlaybackPipeline({ lam, sampleRate: 16000 });\n * avatar.connectFrameSource(pipeline);\n *\n * // In render loop:\n * avatar.update(delta, camera);\n * ```\n *\n * @category Three\n */\n\nimport {\n CharacterController,\n createLogger,\n} from '@omote/core';\nimport type {\n CharacterControllerConfig,\n EmotionWeights,\n ConversationalState,\n FaceCompositorConfig,\n} from '@omote/core';\nimport { Vector3, Quaternion } from 'three';\nimport type { Camera, Object3D } from 'three';\nimport { discoverScene } from './SceneDiscovery';\nimport type { SceneDiscoveryResult } from './SceneDiscovery';\nimport { writeBlendshapes } from './BlendshapeWriter';\n\nconst logger = createLogger('OmoteAvatar');\n\n// Reusable scratch vectors — avoids per-frame allocation\nconst _headWorldPos = new Vector3();\nconst _camWorldPos = new Vector3();\nconst _headWorldQuat = new Quaternion();\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Generic frame source — any object that emits 'frame' events with blendshapes. */\nexport interface FrameSource {\n on(event: 'frame', callback: (frame: { blendshapes: Float32Array }) => void): void;\n off?(event: 'frame', callback: (...args: any[]) => void): void;\n}\n\nexport interface OmoteAvatarOptions {\n /** Three.js Object3D (loaded GLB scene, Group, etc.) to traverse for meshes and bones. */\n target: Object3D;\n /** FaceCompositor configuration (profile, emotion, life layer). */\n compositor?: FaceCompositorConfig;\n /** Gaze tracking configuration. */\n gaze?: {\n enabled?: boolean;\n yawInfluence?: number;\n pitchInfluence?: number;\n smoothing?: number;\n };\n}\n\n// ---------------------------------------------------------------------------\n// OmoteAvatar\n// ---------------------------------------------------------------------------\n\nexport class OmoteAvatar {\n private readonly controller: CharacterController;\n private readonly discovery: SceneDiscoveryResult;\n private frameSourceCallback: ((frame: { blendshapes: Float32Array }) => void) | null = null;\n private connectedSource: FrameSource | null = null;\n\n // State\n private currentBlendshapes: Float32Array | null = null;\n private _emotion: string | EmotionWeights | null = null;\n private _isSpeaking = false;\n private _state: ConversationalState = 'idle';\n private _audioEnergy = 0;\n\n constructor(options: OmoteAvatarOptions) {\n this.discovery = discoverScene(options.target);\n\n const controllerConfig: CharacterControllerConfig = {\n compositor: options.compositor,\n gaze: options.gaze,\n };\n this.controller = new CharacterController(controllerConfig);\n\n if (this.discovery.morphEntries.length === 0) {\n logger.warn('No morph targets found — blendshape animation will have no effect');\n }\n if (!this.discovery.headBone) {\n logger.warn('Head bone not found — gaze tracking will be disabled');\n }\n logger.info(\n `Initialized: ${this.discovery.meshes.length} mesh(es), ${this.discovery.mappedBlendshapeCount} mapped blendshapes, headBone=${!!this.discovery.headBone}`,\n );\n }\n\n // -------------------------------------------------------------------------\n // Per-frame update\n // -------------------------------------------------------------------------\n\n /**\n * Call each frame in your render loop.\n *\n * Runs CharacterController (compositor + gaze + life layer), writes\n * blendshapes to morph targets, and applies head bone rotation.\n *\n * @param delta - Time since last frame in seconds\n * @param camera - The active Three.js camera (used for gaze direction)\n * @param avatarRotationY - Optional avatar Y rotation in radians for gaze compensation\n */\n update(delta: number, camera: Camera, avatarRotationY?: number): void {\n // Compute head world position + quaternion from bone\n let headWorldPos: { x: number; y: number; z: number } | undefined;\n let headWorldQuat: { x: number; y: number; z: number; w: number } | undefined;\n if (this.discovery.headBone?.getWorldPosition) {\n this.discovery.headBone.getWorldPosition(_headWorldPos);\n headWorldPos = { x: _headWorldPos.x, y: _headWorldPos.y, z: _headWorldPos.z };\n this.discovery.headBone.getWorldQuaternion(_headWorldQuat);\n headWorldQuat = { x: _headWorldQuat.x, y: _headWorldQuat.y, z: _headWorldQuat.z, w: _headWorldQuat.w };\n }\n\n // Camera world position (use getWorldPosition for parented cameras)\n camera.getWorldPosition(_camWorldPos);\n const cameraWorldPos = {\n x: _camWorldPos.x,\n y: _camWorldPos.y,\n z: _camWorldPos.z,\n };\n\n const output = this.controller.update({\n deltaTime: delta,\n baseBlendshapes: this.currentBlendshapes,\n emotion: this._emotion,\n isSpeaking: this._isSpeaking,\n state: this._state,\n audioEnergy: this._audioEnergy,\n cameraWorldPos,\n headWorldPos,\n headWorldQuat,\n avatarRotationY: avatarRotationY ?? 0,\n });\n\n // Write blendshapes to morph targets\n writeBlendshapes(output.blendshapes, this.discovery.morphEntries);\n\n // Apply head rotation from gaze + life layer\n if (this.discovery.headBone) {\n this.discovery.headBone.rotation.y = output.headDelta.yaw;\n this.discovery.headBone.rotation.x = output.headDelta.pitch;\n }\n }\n\n // -------------------------------------------------------------------------\n // Frame source connection\n // -------------------------------------------------------------------------\n\n /**\n * Connect to any frame source (PlaybackPipeline, MicLipSync, etc.).\n * Listens for 'frame' events and updates current blendshapes automatically.\n *\n * Only one source can be connected at a time. Connecting a new source\n * disconnects the previous one.\n */\n connectFrameSource(source: FrameSource): void {\n // Disconnect existing source first\n this.disconnectFrameSource();\n\n this.frameSourceCallback = (frame: { blendshapes: Float32Array }) => {\n this.currentBlendshapes = frame.blendshapes;\n };\n source.on('frame', this.frameSourceCallback);\n this.connectedSource = source;\n logger.debug('Frame source connected');\n }\n\n /** Disconnect the currently connected frame source. */\n disconnectFrameSource(): void {\n if (this.connectedSource && this.frameSourceCallback) {\n if (this.connectedSource.off) {\n this.connectedSource.off('frame', this.frameSourceCallback);\n }\n logger.debug('Frame source disconnected');\n }\n this.connectedSource = null;\n this.frameSourceCallback = null;\n }\n\n // -------------------------------------------------------------------------\n // State setters\n // -------------------------------------------------------------------------\n\n /** Set raw blendshapes directly (alternative to connectFrameSource). */\n setFrame(blendshapes: Float32Array): void {\n this.currentBlendshapes = blendshapes;\n }\n\n /** Set the current emotion (string preset name or EmotionWeights object). */\n setEmotion(emotion: string | EmotionWeights): void {\n this._emotion = emotion;\n }\n\n /** Set whether the avatar is currently speaking (drives mouth animation intensity). */\n setSpeaking(speaking: boolean): void {\n this._isSpeaking = speaking;\n }\n\n /** Set the conversational state (idle, listening, thinking, speaking). */\n setState(state: ConversationalState): void {\n this._state = state;\n }\n\n /** Set audio energy level (0-1, drives emphasis/gesture intensity). */\n setAudioEnergy(energy: number): void {\n this._audioEnergy = energy;\n }\n\n // -------------------------------------------------------------------------\n // Accessors\n // -------------------------------------------------------------------------\n\n /** Access the underlying FaceCompositor for advanced configuration. */\n get compositor() {\n return this.controller.compositor;\n }\n\n /** Access discovered scene parts (meshes, bones). */\n get parts(): SceneDiscoveryResult {\n return this.discovery;\n }\n\n /** Whether the scene has any mapped morph targets. */\n get hasMorphTargets(): boolean {\n return this.discovery.morphEntries.length > 0;\n }\n\n /** Number of successfully mapped ARKit blendshapes. */\n get mappedBlendshapeCount(): number {\n return this.discovery.mappedBlendshapeCount;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n /** Reset all state (smoothing, life layer, emotions). */\n reset(): void {\n this.currentBlendshapes = null;\n this._emotion = null;\n this._isSpeaking = false;\n this._state = 'idle';\n this._audioEnergy = 0;\n this.controller.reset();\n }\n\n /** Disconnect frame sources and dispose the controller. */\n dispose(): void {\n this.disconnectFrameSource();\n this.controller.dispose();\n logger.debug('Disposed');\n }\n}\n","/**\n * SceneDiscovery — Traverse a Three.js scene graph to discover bones and\n * morph targets. Pure function, no React or side-effect dependencies.\n *\n * This is the CANONICAL implementation. @omote/r3f imports from here.\n *\n * Public interfaces use duck types (no direct Three.js type imports) so\n * consumers with different @types/three versions avoid type-incompatibility\n * errors in monorepo setups.\n *\n * @category Three\n */\n\nimport { LAM_BLENDSHAPES, createLogger } from '@omote/core';\n\nconst logger = createLogger('SceneDiscovery');\n\n// ---------------------------------------------------------------------------\n// Duck-typed interfaces — structurally compatible with Three.js but\n// importable without pulling in @types/three.\n// ---------------------------------------------------------------------------\n\n/** Any object with `traverse` (structurally compatible with THREE.Object3D). */\nexport interface SceneObject {\n traverse(callback: (child: any) => void): void;\n}\n\n/** Minimal shape of a skinned mesh. */\nexport interface DiscoveredMesh {\n name: string;\n isSkinnedMesh?: boolean;\n morphTargetDictionary?: Record<string, number>;\n morphTargetInfluences?: number[];\n [key: string]: any;\n}\n\n/** Minimal shape of a bone. */\nexport interface DiscoveredBone {\n name: string;\n isBone?: boolean;\n rotation: { x: number; y: number; z: number };\n getWorldPosition?(target: any): any;\n [key: string]: any;\n}\n\n/** Pre-computed morph target index array for a single mesh */\nexport interface MorphIndexEntry {\n mesh: DiscoveredMesh;\n /** indices[lamIndex] = morphTargetIndex (or -1 if not found) */\n indices: Int16Array;\n}\n\nexport interface SceneDiscoveryResult {\n meshes: DiscoveredMesh[];\n headBone: DiscoveredBone | null;\n neckBone: DiscoveredBone | null;\n leftEyeBone: DiscoveredBone | null;\n rightEyeBone: DiscoveredBone | null;\n /** Pre-computed morph index arrays per mesh (for zero-lookup hot path) */\n morphEntries: MorphIndexEntry[];\n /** Primary face mesh (prefer 'Head_Mesh', fallback to first mesh with morph targets) */\n faceMesh: DiscoveredMesh | null;\n /** Number of successfully mapped ARKit blendshapes */\n mappedBlendshapeCount: number;\n}\n\n/**\n * Traverse a Three.js scene to discover bones and morph targets.\n * Pure function — no React, no side effects.\n *\n * Finds all SkinnedMesh nodes and named bones (Head, Neck, LeftEye, RightEye).\n * Pre-computes morph index arrays using LAM_BLENDSHAPES for zero-lookup\n * hot-path blendshape writing.\n */\nexport function discoverScene(scene: SceneObject): SceneDiscoveryResult {\n const meshes: DiscoveredMesh[] = [];\n let headBone: DiscoveredBone | null = null;\n let neckBone: DiscoveredBone | null = null;\n let leftEyeBone: DiscoveredBone | null = null;\n let rightEyeBone: DiscoveredBone | null = null;\n\n scene.traverse((child: any) => {\n if (child.isSkinnedMesh) {\n meshes.push(child as DiscoveredMesh);\n }\n if (child.isBone) {\n switch (child.name) {\n case 'Head': headBone = child as DiscoveredBone; break;\n case 'Neck': neckBone = child as DiscoveredBone; break;\n case 'LeftEye': leftEyeBone = child as DiscoveredBone; break;\n case 'RightEye': rightEyeBone = child as DiscoveredBone; break;\n }\n }\n });\n\n // Pre-compute morph target index arrays (hot path optimization)\n const morphEntries: MorphIndexEntry[] = [];\n let mappedCount = 0;\n\n for (const mesh of meshes) {\n if (!mesh.morphTargetDictionary || !mesh.morphTargetInfluences) continue;\n\n const indices = new Int16Array(LAM_BLENDSHAPES.length).fill(-1);\n let meshMapped = 0;\n\n for (let i = 0; i < LAM_BLENDSHAPES.length; i++) {\n const morphIdx = mesh.morphTargetDictionary[LAM_BLENDSHAPES[i]];\n if (morphIdx !== undefined) {\n indices[i] = morphIdx;\n meshMapped++;\n }\n }\n\n if (meshMapped > 0) {\n morphEntries.push({ mesh, indices });\n mappedCount = Math.max(mappedCount, meshMapped);\n }\n\n logger.debug(`Mesh \"${mesh.name}\": ${meshMapped}/${LAM_BLENDSHAPES.length} blendshapes mapped`);\n }\n\n const faceMesh = meshes.find(m => m.name === 'Head_Mesh' && m.morphTargetDictionary)\n ?? meshes.find(m => m.morphTargetDictionary)\n ?? null;\n\n if (morphEntries.length === 0) {\n logger.warn('No morph targets found in scene');\n }\n if (!headBone) {\n logger.warn('Head bone not found in scene');\n }\n\n const boneNames = [\n headBone && 'Head',\n neckBone && 'Neck',\n leftEyeBone && 'LeftEye',\n rightEyeBone && 'RightEye',\n ].filter(Boolean);\n\n logger.info(\n `Discovery complete: ${meshes.length} mesh(es), ${mappedCount} mapped blendshapes, bones: [${boneNames.join(', ')}]`,\n );\n\n return {\n meshes, headBone, neckBone, leftEyeBone, rightEyeBone,\n morphEntries, faceMesh, mappedBlendshapeCount: mappedCount,\n };\n}\n","/**\n * BlendshapeWriter — Write Float32Array[52] blendshapes to morph target\n * influences using pre-computed index arrays.\n *\n * Zero-lookup hot path: iterates a flat Int16Array per mesh instead\n * of doing string-keyed dictionary lookups every frame.\n *\n * @category Three\n */\n\nimport type { MorphIndexEntry } from './SceneDiscovery';\n\n/**\n * Write 52 ARKit blendshapes to morph target influences.\n * Uses pre-computed index arrays for zero-lookup hot path.\n *\n * @param blendshapes - Float32Array of 52 ARKit blendshape weights\n * @param morphEntries - Pre-computed morph index entries from discoverScene()\n */\nexport function writeBlendshapes(\n blendshapes: Float32Array,\n morphEntries: MorphIndexEntry[],\n): void {\n for (let e = 0; e < morphEntries.length; e++) {\n const { mesh, indices } = morphEntries[e];\n const influences = mesh.morphTargetInfluences;\n if (!influences) continue;\n for (let i = 0; i < 52; i++) {\n const morphIdx = indices[i];\n if (morphIdx >= 0) {\n influences[morphIdx] = blendshapes[i];\n }\n }\n }\n}\n","import { LAM_BLENDSHAPES, lerpBlendshapes } from '@omote/core';\nimport type { Object3D, SkinnedMesh } from 'three';\n\nexport interface BlendshapeControllerOptions {\n /** Blendshape names in order (default: LAM_BLENDSHAPES, 52 ARKit) */\n names?: readonly string[];\n /** Smoothing factor 0-1 (0 = no change, 1 = snap to target). Default: 0.7 */\n smoothing?: number;\n /** Traverse target for SkinnedMesh children automatically. Default: true */\n autoFind?: boolean;\n /** Called when meshes with morph targets are found */\n onMeshesFound?: (meshes: SkinnedMesh[]) => void;\n}\n\nexport class BlendshapeController {\n private _meshes: SkinnedMesh[] = [];\n private nameToIndex: Map<string, number>[] = [];\n private currentWeights: number[] = [];\n private blendshapeNames: readonly string[];\n private smoothing: number;\n private onMeshesFound?: (meshes: SkinnedMesh[]) => void;\n\n constructor(target: Object3D, options?: BlendshapeControllerOptions) {\n this.blendshapeNames = options?.names ?? LAM_BLENDSHAPES;\n this.smoothing = options?.smoothing ?? 0.7;\n this.onMeshesFound = options?.onMeshesFound;\n if (options?.autoFind !== false) {\n this.setTarget(target);\n }\n }\n\n get meshes(): SkinnedMesh[] {\n return this._meshes;\n }\n\n setTarget(target: Object3D): void {\n this._meshes = [];\n this.nameToIndex = [];\n\n target.traverse((child) => {\n const mesh = child as SkinnedMesh;\n if (mesh.morphTargetInfluences && mesh.morphTargetDictionary) {\n this._meshes.push(mesh);\n\n const map = new Map<string, number>();\n for (let i = 0; i < this.blendshapeNames.length; i++) {\n const name = this.blendshapeNames[i];\n const idx = mesh.morphTargetDictionary[name];\n if (idx !== undefined) {\n map.set(name, idx);\n }\n }\n this.nameToIndex.push(map);\n }\n });\n\n this.currentWeights = new Array(this.blendshapeNames.length).fill(0);\n\n if (this._meshes.length > 0 && this.onMeshesFound) {\n this.onMeshesFound(this._meshes);\n }\n }\n\n update(weights: Float32Array | number[]): void {\n this.currentWeights = lerpBlendshapes(this.currentWeights, weights, this.smoothing);\n\n for (let m = 0; m < this._meshes.length; m++) {\n const mesh = this._meshes[m];\n const map = this.nameToIndex[m];\n if (!mesh.morphTargetInfluences || !map) continue;\n\n for (let i = 0; i < this.blendshapeNames.length; i++) {\n const name = this.blendshapeNames[i];\n const dictIdx = map.get(name);\n if (dictIdx !== undefined) {\n mesh.morphTargetInfluences[dictIdx] = this.currentWeights[i];\n }\n }\n }\n }\n\n dispose(): void {\n this._meshes = [];\n this.nameToIndex = [];\n this.currentWeights = [];\n }\n}\n","import { A2EOrchestrator } from '@omote/core';\nimport type { A2EOrchestratorConfig } from '@omote/core';\nimport type { Object3D } from 'three';\nimport { BlendshapeController } from './BlendshapeController';\nimport type { BlendshapeControllerOptions } from './BlendshapeController';\n\nexport interface OmoteA2EOptions extends A2EOrchestratorConfig {\n target: Object3D;\n controllerOptions?: BlendshapeControllerOptions;\n}\n\n/**\n * @deprecated Use {@link OmoteAvatar} instead. OmoteA2E will be removed in v0.8.0.\n */\nexport class OmoteA2E {\n private orchestrator: A2EOrchestrator;\n private controller: BlendshapeController;\n\n constructor(options: OmoteA2EOptions) {\n const { target, controllerOptions, ...orchestratorConfig } = options;\n this.controller = new BlendshapeController(target, controllerOptions);\n this.orchestrator = new A2EOrchestrator(orchestratorConfig);\n }\n\n async load(): Promise<void> { return this.orchestrator.load(); }\n async start(): Promise<void> { return this.orchestrator.start(); }\n stop(): void { this.orchestrator.stop(); }\n\n update(): void {\n const w = this.orchestrator.latestWeights;\n if (w) this.controller.update(w);\n }\n\n async dispose(): Promise<void> {\n await this.orchestrator.dispose();\n this.controller.dispose();\n }\n\n get isReady(): boolean { return this.orchestrator.isReady; }\n get isStreaming(): boolean { return this.orchestrator.isStreaming; }\n get backend(): string | null { return this.orchestrator.backend; }\n}\n"],"mappings":"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","names":["import_core","logger","import_core","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/OmoteAvatar.ts","../src/SceneDiscovery.ts","../src/BlendshapeWriter.ts","../src/BlendshapeController.ts"],"sourcesContent":["// High-level\nexport { OmoteAvatar } from './OmoteAvatar';\nexport type { OmoteAvatarOptions, FrameSource } from './OmoteAvatar';\n\n// Re-export TTSSpeakerConfig as TTSConfig for convenience\nexport type { TTSSpeakerConfig as TTSConfig } from '@omote/core';\n\n// Scene utilities (canonical — r3f imports from here)\nexport { discoverScene } from './SceneDiscovery';\nexport type { SceneDiscoveryResult, MorphIndexEntry, SceneObject, DiscoveredMesh, DiscoveredBone } from './SceneDiscovery';\nexport { writeBlendshapes } from './BlendshapeWriter';\n\n// Low-level escape hatch\nexport { BlendshapeController } from './BlendshapeController';\nexport type { BlendshapeControllerOptions } from './BlendshapeController';\n\n","/**\n * OmoteAvatar — Full-featured Three.js avatar class.\n *\n * Wraps CharacterController from @omote/core with Three.js scene discovery,\n * blendshape writing, and gaze bone rotation. Drop-in avatar for any\n * Three.js app that has a render loop.\n *\n * @example\n * ```ts\n * import { OmoteAvatar } from '@omote/three';\n * import { createA2E, PlaybackPipeline } from '@omote/core';\n *\n * const avatar = new OmoteAvatar({ target: avatarModel });\n * const lam = createA2E();\n * await lam.load();\n * const pipeline = new PlaybackPipeline({ lam, sampleRate: 16000 });\n * avatar.connectFrameSource(pipeline);\n *\n * // In render loop:\n * avatar.update(delta, camera);\n * ```\n *\n * @example Speaker integration\n * ```ts\n * const avatar = new OmoteAvatar({ target: avatarModel });\n * await avatar.connectSpeaker(myTTSBackend, { profile: { mouth: 1.2 } });\n * await avatar.speak(\"Hello world!\"); // lip-syncs automatically\n * ```\n *\n * @category Three\n */\n\nimport {\n CharacterController,\n TTSSpeaker,\n SpeechListener,\n VoiceOrchestrator,\n createLogger,\n} from '@omote/core';\nimport type {\n CharacterControllerConfig,\n CharacterProfile,\n EmotionWeights,\n ConversationalState,\n FaceCompositorConfig,\n FrameSource,\n TTSSpeakerConfig,\n TTSBackend,\n SpeechListenerConfig,\n TranscriptResult,\n VoiceOrchestratorConfig,\n} from '@omote/core';\nimport { Vector3, Quaternion } from 'three';\nimport type { Camera, Object3D } from 'three';\nimport { discoverScene } from './SceneDiscovery';\nimport type { SceneDiscoveryResult } from './SceneDiscovery';\nimport { writeBlendshapes } from './BlendshapeWriter';\n\nconst logger = createLogger('OmoteAvatar');\n\n// Reusable scratch vectors — avoids per-frame allocation\nconst _headWorldPos = new Vector3();\nconst _camWorldPos = new Vector3();\nconst _headWorldQuat = new Quaternion();\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n// Re-export FrameSource from @omote/core for backward compatibility\nexport type { FrameSource } from '@omote/core';\n\nexport interface OmoteAvatarOptions {\n /** Three.js Object3D (loaded GLB scene, Group, etc.) to traverse for meshes and bones. */\n target: Object3D;\n /** FaceCompositor configuration (profile, emotion, life layer). */\n compositor?: FaceCompositorConfig;\n /** Gaze tracking configuration. */\n gaze?: CharacterControllerConfig['gaze'];\n}\n\n// ---------------------------------------------------------------------------\n// OmoteAvatar\n// ---------------------------------------------------------------------------\n\nexport class OmoteAvatar {\n private readonly controller: CharacterController;\n private readonly discovery: SceneDiscoveryResult;\n private frameSourceCallback: ((frame: { blendshapes: Float32Array }) => void) | null = null;\n private connectedSource: FrameSource | null = null;\n\n // TTS integration\n private ttsSpeaker: TTSSpeaker | null = null;\n\n // Speech listener\n private speechListener: SpeechListener | null = null;\n\n // Voice orchestrator\n private voiceOrchestrator: VoiceOrchestrator | null = null;\n\n // State\n private currentBlendshapes: Float32Array | null = null;\n private _emotion: string | EmotionWeights | null = null;\n private _isSpeaking = false;\n private _state: ConversationalState = 'idle';\n private _audioEnergy = 0;\n\n constructor(options: OmoteAvatarOptions) {\n this.discovery = discoverScene(options.target);\n\n const controllerConfig: CharacterControllerConfig = {\n compositor: options.compositor,\n gaze: options.gaze,\n };\n this.controller = new CharacterController(controllerConfig);\n\n if (this.discovery.morphEntries.length === 0) {\n logger.warn('No morph targets found — blendshape animation will have no effect');\n }\n if (!this.discovery.headBone) {\n logger.warn('Head bone not found — gaze tracking will be disabled');\n }\n logger.info(\n `Initialized: ${this.discovery.meshes.length} mesh(es), ${this.discovery.mappedBlendshapeCount} mapped blendshapes, headBone=${!!this.discovery.headBone}`,\n );\n }\n\n // -------------------------------------------------------------------------\n // Per-frame update\n // -------------------------------------------------------------------------\n\n /**\n * Call each frame in your render loop.\n *\n * Runs CharacterController (compositor + gaze + life layer), writes\n * blendshapes to morph targets, and applies head bone rotation.\n *\n * @param delta - Time since last frame in seconds\n * @param camera - The active Three.js camera (used for gaze direction)\n * @param avatarRotationY - Optional avatar Y rotation in radians for gaze compensation\n */\n update(delta: number, camera: Camera, avatarRotationY?: number): void {\n // Compute head world position + quaternion from bone\n let headWorldPos: { x: number; y: number; z: number } | undefined;\n let headWorldQuat: { x: number; y: number; z: number; w: number } | undefined;\n if (this.discovery.headBone?.getWorldPosition) {\n this.discovery.headBone.getWorldPosition(_headWorldPos);\n headWorldPos = { x: _headWorldPos.x, y: _headWorldPos.y, z: _headWorldPos.z };\n this.discovery.headBone.getWorldQuaternion(_headWorldQuat);\n headWorldQuat = { x: _headWorldQuat.x, y: _headWorldQuat.y, z: _headWorldQuat.z, w: _headWorldQuat.w };\n }\n\n // Camera world position (use getWorldPosition for parented cameras)\n camera.getWorldPosition(_camWorldPos);\n const cameraWorldPos = {\n x: _camWorldPos.x,\n y: _camWorldPos.y,\n z: _camWorldPos.z,\n };\n\n const output = this.controller.update({\n deltaTime: delta,\n baseBlendshapes: this.currentBlendshapes,\n emotion: this._emotion,\n isSpeaking: this._isSpeaking,\n state: this._state,\n audioEnergy: this._audioEnergy,\n cameraWorldPos,\n headWorldPos,\n headWorldQuat,\n avatarRotationY: avatarRotationY ?? 0,\n });\n\n // Write blendshapes to morph targets\n writeBlendshapes(output.blendshapes, this.discovery.morphEntries);\n\n // Apply head rotation from gaze + life layer\n if (this.discovery.headBone) {\n this.discovery.headBone.rotation.y = output.headDelta.yaw;\n this.discovery.headBone.rotation.x = output.headDelta.pitch;\n }\n }\n\n // -------------------------------------------------------------------------\n // Frame source connection\n // -------------------------------------------------------------------------\n\n /**\n * Connect to any frame source (PlaybackPipeline, MicLipSync, etc.).\n * Listens for 'frame' events and updates current blendshapes automatically.\n *\n * Only one source can be connected at a time. Connecting a new source\n * disconnects the previous one.\n */\n connectFrameSource(source: FrameSource): void {\n // If connecting a different source while TTS is active, stop current speak\n if (this.ttsSpeaker && source !== this.ttsSpeaker.frameSource) {\n this.ttsSpeaker.stop();\n }\n\n // Disconnect existing source first\n this.disconnectFrameSource();\n\n this.frameSourceCallback = (frame: { blendshapes: Float32Array; emotion?: string }) => {\n this.currentBlendshapes = frame.blendshapes;\n if (frame.emotion !== undefined) {\n this._emotion = frame.emotion;\n }\n };\n source.on('frame', this.frameSourceCallback);\n this.connectedSource = source;\n logger.debug('Frame source connected');\n }\n\n /** Disconnect the currently connected frame source. */\n disconnectFrameSource(): void {\n if (this.connectedSource && this.frameSourceCallback) {\n if (this.connectedSource.off) {\n this.connectedSource.off('frame', this.frameSourceCallback);\n }\n logger.debug('Frame source disconnected');\n }\n this.connectedSource = null;\n this.frameSourceCallback = null;\n }\n\n // -------------------------------------------------------------------------\n // Speaker (TTS → lip sync)\n // -------------------------------------------------------------------------\n\n /**\n * Connect a TTS backend for speak() / streamText() support.\n * Loads LAM model and creates internal PlaybackPipeline.\n *\n * @param tts - TTS backend (e.g., KokoroTTSInference, ElevenLabs adapter)\n * @param config - A2E, expression profile, and playback configuration\n */\n async connectSpeaker(tts: TTSBackend, config?: TTSSpeakerConfig): Promise<void> {\n await this.disconnectSpeaker();\n this.ttsSpeaker = new TTSSpeaker();\n await this.ttsSpeaker.connect(tts, config);\n this.connectFrameSource(this.ttsSpeaker.frameSource!);\n }\n\n /**\n * Synthesize text and play with lip sync.\n * Auto-aborts previous speak if still in progress.\n *\n * @param text - Text to synthesize\n * @param options - Optional voice override and abort signal\n */\n async speak(text: string, options?: { signal?: AbortSignal; voice?: string }): Promise<void> {\n if (this.voiceOrchestrator) {\n await this.voiceOrchestrator.speak(text, options);\n return;\n }\n if (!this.ttsSpeaker) {\n throw new Error('No speaker connected. Call connectSpeaker() first.');\n }\n this._isSpeaking = true;\n this._state = 'speaking';\n try {\n await this.ttsSpeaker.speak(text, options);\n } finally {\n this._isSpeaking = false;\n if (this._state === 'speaking') {\n this._state = 'idle';\n }\n }\n }\n\n /**\n * Stream LLM tokens with sentence-buffered TTS + lip sync.\n * Returns a sink: call push(token) for each token, end() when done.\n */\n async streamText(options?: { signal?: AbortSignal; voice?: string }): Promise<{\n push: (token: string) => void;\n end: () => Promise<void>;\n }> {\n if (this.voiceOrchestrator) {\n return this.voiceOrchestrator.streamText(options);\n }\n if (!this.ttsSpeaker) {\n throw new Error('No speaker connected. Call connectSpeaker() first.');\n }\n this._isSpeaking = true;\n this._state = 'speaking';\n const stream = await this.ttsSpeaker.streamText(options ?? {});\n return {\n push: stream.push,\n end: async () => {\n try { await stream.end(); }\n finally { this._isSpeaking = false; if (this._state === 'speaking') this._state = 'idle'; }\n },\n };\n }\n\n /** Stop current TTS playback. */\n stopSpeaking(): void {\n if (this.voiceOrchestrator) {\n this.voiceOrchestrator.stopSpeaking();\n return;\n }\n this.ttsSpeaker?.stop();\n }\n\n /** Disconnect speaker and dispose its resources. */\n async disconnectSpeaker(): Promise<void> {\n if (this.ttsSpeaker) {\n this.disconnectFrameSource();\n await this.ttsSpeaker.dispose();\n this.ttsSpeaker = null;\n }\n }\n\n /** @deprecated Use connectSpeaker(). Will be removed in v1.0. */\n async connectTTS(tts: TTSBackend, config?: TTSSpeakerConfig): Promise<void> {\n return this.connectSpeaker(tts, config);\n }\n\n /** @deprecated Use disconnectSpeaker(). Will be removed in v1.0. */\n async disconnectTTS(): Promise<void> {\n return this.disconnectSpeaker();\n }\n\n // -------------------------------------------------------------------------\n // Listener (mic → VAD → ASR → transcript)\n // -------------------------------------------------------------------------\n\n /**\n * Connect a speech listener for startListening() / onTranscript() support.\n * Loads ASR + VAD models.\n */\n async connectListener(config?: SpeechListenerConfig): Promise<void> {\n await this.disconnectListener();\n this.speechListener = new SpeechListener(config);\n await this.speechListener.loadModels();\n }\n\n /** Start listening for user speech. Requires connectListener() or connectVoice() first. */\n async startListening(): Promise<void> {\n if (this.voiceOrchestrator) {\n await this.voiceOrchestrator.startListening();\n return;\n }\n if (!this.speechListener) {\n throw new Error('No listener connected. Call connectListener() first.');\n }\n this._state = 'listening';\n await this.speechListener.start();\n }\n\n /** Stop listening. */\n stopListening(): void {\n if (this.voiceOrchestrator) {\n this.voiceOrchestrator.stopListening();\n return;\n }\n this.speechListener?.stop();\n if (this._state === 'listening') this._state = 'idle';\n }\n\n /**\n * Subscribe to transcript events. Returns an unsubscribe function.\n * Requires connectListener() first.\n */\n onTranscript(callback: (result: TranscriptResult) => void): () => void {\n const listener = this.speechListener ?? this.voiceOrchestrator?.listener;\n if (!listener) {\n throw new Error('No listener connected. Call connectListener() or connectVoice() first.');\n }\n listener.on('transcript', callback);\n return () => { listener.off?.('transcript', callback); };\n }\n\n /** Disconnect listener and dispose its resources. */\n async disconnectListener(): Promise<void> {\n if (this.speechListener) {\n await this.speechListener.dispose();\n this.speechListener = null;\n }\n }\n\n // -------------------------------------------------------------------------\n // Voice (combined speaker + listener + interruption)\n // -------------------------------------------------------------------------\n\n /**\n * Connect voice with automatic speaker + listener + interruption wiring.\n * Supports both local TTS (mode: 'local') and cloud TTS (mode: 'cloud').\n * Does NOT auto-start listening — call startListening() when ready.\n *\n * Backward compatible: `mode` defaults to 'local' when not specified.\n */\n async connectVoice(config: VoiceOrchestratorConfig): Promise<void> {\n await this.disconnectVoice();\n this.voiceOrchestrator = new VoiceOrchestrator();\n await this.voiceOrchestrator.connect(config);\n\n // Connect frame source from orchestrator\n if (this.voiceOrchestrator.frameSource) {\n this.connectFrameSource(this.voiceOrchestrator.frameSource);\n }\n\n // Sync state from orchestrator → avatar\n this.voiceOrchestrator.on('state', (state) => {\n this._state = state;\n this._isSpeaking = state === 'speaking';\n });\n }\n\n /** Disconnect voice (speaker + listener + interruption). */\n async disconnectVoice(): Promise<void> {\n if (this.voiceOrchestrator) {\n this.disconnectFrameSource();\n await this.voiceOrchestrator.disconnect();\n this.voiceOrchestrator = null;\n }\n }\n\n // -------------------------------------------------------------------------\n // State setters\n // -------------------------------------------------------------------------\n\n /** Set raw blendshapes directly (alternative to connectFrameSource). */\n setFrame(blendshapes: Float32Array): void {\n this.currentBlendshapes = blendshapes;\n }\n\n /** Set the current emotion (string preset name or EmotionWeights object). */\n setEmotion(emotion: string | EmotionWeights): void {\n this._emotion = emotion;\n }\n\n /** Set whether the avatar is currently speaking (drives mouth animation intensity). */\n setSpeaking(speaking: boolean): void {\n this._isSpeaking = speaking;\n }\n\n /** Set the conversational state (idle, listening, thinking, speaking). */\n setState(state: ConversationalState): void {\n this._state = state;\n }\n\n /** Set audio energy level (0-1, drives emphasis/gesture intensity). */\n setAudioEnergy(energy: number): void {\n this._audioEnergy = energy;\n }\n\n /** Update character expression profile at runtime. */\n setProfile(profile: CharacterProfile): void {\n this.controller.setProfile(profile);\n }\n\n // -------------------------------------------------------------------------\n // Accessors\n // -------------------------------------------------------------------------\n\n /** Access the underlying FaceCompositor for advanced configuration. */\n get compositor() {\n return this.controller.compositor;\n }\n\n /** Access discovered scene parts (meshes, bones). */\n get parts(): SceneDiscoveryResult {\n return this.discovery;\n }\n\n /** Whether the scene has any mapped morph targets. */\n get hasMorphTargets(): boolean {\n return this.discovery.morphEntries.length > 0;\n }\n\n /** Number of successfully mapped ARKit blendshapes. */\n get mappedBlendshapeCount(): number {\n return this.discovery.mappedBlendshapeCount;\n }\n\n /** Whether the avatar is currently speaking via TTS. */\n get isSpeaking(): boolean {\n return this._isSpeaking;\n }\n\n /** Whether the avatar is currently listening for speech. */\n get isListening(): boolean {\n return this._state === 'listening';\n }\n\n /** Current conversational state. */\n get conversationalState(): ConversationalState {\n return this._state;\n }\n\n /** Access the internal TTSSpeaker (null if not connected). */\n get speaker(): TTSSpeaker | null {\n return this.ttsSpeaker ?? this.voiceOrchestrator?.speaker ?? null;\n }\n\n /** Access the internal SpeechListener (null if not connected). */\n get listener(): SpeechListener | null {\n return this.speechListener ?? this.voiceOrchestrator?.listener ?? null;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n /** Reset all state (smoothing, life layer, emotions). */\n reset(): void {\n this.currentBlendshapes = null;\n this._emotion = null;\n this._isSpeaking = false;\n this._state = 'idle';\n this._audioEnergy = 0;\n this.controller.reset();\n }\n\n /** Disconnect all voice resources, frame sources, and dispose the controller. */\n async dispose(): Promise<void> {\n await this.disconnectVoice();\n await this.disconnectSpeaker();\n await this.disconnectListener();\n this.disconnectFrameSource();\n this.controller.dispose();\n logger.debug('Disposed');\n }\n}\n\n","/**\n * SceneDiscovery — Traverse a Three.js scene graph to discover bones and\n * morph targets. Pure function, no React or side-effect dependencies.\n *\n * This is the CANONICAL implementation. @omote/r3f imports from here.\n *\n * Public interfaces use duck types (no direct Three.js type imports) so\n * consumers with different @types/three versions avoid type-incompatibility\n * errors in monorepo setups.\n *\n * @category Three\n */\n\nimport { LAM_BLENDSHAPES, createLogger } from '@omote/core';\n\nconst logger = createLogger('SceneDiscovery');\n\n// ---------------------------------------------------------------------------\n// Duck-typed interfaces — structurally compatible with Three.js but\n// importable without pulling in @types/three.\n// ---------------------------------------------------------------------------\n\n/** Any object with `traverse` (structurally compatible with THREE.Object3D). */\nexport interface SceneObject {\n traverse(callback: (child: any) => void): void;\n}\n\n/** Minimal shape of a skinned mesh. */\nexport interface DiscoveredMesh {\n name: string;\n isSkinnedMesh?: boolean;\n morphTargetDictionary?: Record<string, number>;\n morphTargetInfluences?: number[];\n [key: string]: any;\n}\n\n/** Minimal shape of a bone. */\nexport interface DiscoveredBone {\n name: string;\n isBone?: boolean;\n rotation: { x: number; y: number; z: number };\n getWorldPosition?(target: any): any;\n [key: string]: any;\n}\n\n/** Pre-computed morph target index array for a single mesh */\nexport interface MorphIndexEntry {\n mesh: DiscoveredMesh;\n /** indices[lamIndex] = morphTargetIndex (or -1 if not found) */\n indices: Int16Array;\n}\n\nexport interface SceneDiscoveryResult {\n meshes: DiscoveredMesh[];\n headBone: DiscoveredBone | null;\n neckBone: DiscoveredBone | null;\n leftEyeBone: DiscoveredBone | null;\n rightEyeBone: DiscoveredBone | null;\n /** Pre-computed morph index arrays per mesh (for zero-lookup hot path) */\n morphEntries: MorphIndexEntry[];\n /** Primary face mesh (prefer 'Head_Mesh', fallback to first mesh with morph targets) */\n faceMesh: DiscoveredMesh | null;\n /** Number of successfully mapped ARKit blendshapes */\n mappedBlendshapeCount: number;\n}\n\n/**\n * Traverse a Three.js scene to discover bones and morph targets.\n * Pure function — no React, no side effects.\n *\n * Finds all SkinnedMesh nodes and named bones (Head, Neck, LeftEye, RightEye).\n * Pre-computes morph index arrays using LAM_BLENDSHAPES for zero-lookup\n * hot-path blendshape writing.\n */\nexport function discoverScene(scene: SceneObject): SceneDiscoveryResult {\n const meshes: DiscoveredMesh[] = [];\n let headBone: DiscoveredBone | null = null;\n let neckBone: DiscoveredBone | null = null;\n let leftEyeBone: DiscoveredBone | null = null;\n let rightEyeBone: DiscoveredBone | null = null;\n\n scene.traverse((child: any) => {\n if (child.isSkinnedMesh) {\n meshes.push(child as DiscoveredMesh);\n }\n if (child.isBone) {\n switch (child.name) {\n case 'Head': headBone = child as DiscoveredBone; break;\n case 'Neck': neckBone = child as DiscoveredBone; break;\n case 'LeftEye': leftEyeBone = child as DiscoveredBone; break;\n case 'RightEye': rightEyeBone = child as DiscoveredBone; break;\n }\n }\n });\n\n // Pre-compute morph target index arrays (hot path optimization)\n const morphEntries: MorphIndexEntry[] = [];\n let mappedCount = 0;\n\n for (const mesh of meshes) {\n if (!mesh.morphTargetDictionary || !mesh.morphTargetInfluences) continue;\n\n const indices = new Int16Array(LAM_BLENDSHAPES.length).fill(-1);\n let meshMapped = 0;\n\n for (let i = 0; i < LAM_BLENDSHAPES.length; i++) {\n const morphIdx = mesh.morphTargetDictionary[LAM_BLENDSHAPES[i]];\n if (morphIdx !== undefined) {\n indices[i] = morphIdx;\n meshMapped++;\n }\n }\n\n if (meshMapped > 0) {\n morphEntries.push({ mesh, indices });\n mappedCount = Math.max(mappedCount, meshMapped);\n }\n\n logger.debug(`Mesh \"${mesh.name}\": ${meshMapped}/${LAM_BLENDSHAPES.length} blendshapes mapped`);\n }\n\n const faceMesh = meshes.find(m => m.name === 'Head_Mesh' && m.morphTargetDictionary)\n ?? meshes.find(m => m.morphTargetDictionary)\n ?? null;\n\n if (morphEntries.length === 0) {\n logger.warn('No morph targets found in scene');\n }\n if (!headBone) {\n logger.warn('Head bone not found in scene');\n }\n\n const boneNames = [\n headBone && 'Head',\n neckBone && 'Neck',\n leftEyeBone && 'LeftEye',\n rightEyeBone && 'RightEye',\n ].filter(Boolean);\n\n logger.info(\n `Discovery complete: ${meshes.length} mesh(es), ${mappedCount} mapped blendshapes, bones: [${boneNames.join(', ')}]`,\n );\n\n return {\n meshes, headBone, neckBone, leftEyeBone, rightEyeBone,\n morphEntries, faceMesh, mappedBlendshapeCount: mappedCount,\n };\n}\n","/**\n * BlendshapeWriter — Write Float32Array[52] blendshapes to morph target\n * influences using pre-computed index arrays.\n *\n * Zero-lookup hot path: iterates a flat Int16Array per mesh instead\n * of doing string-keyed dictionary lookups every frame.\n *\n * @category Three\n */\n\nimport type { MorphIndexEntry } from './SceneDiscovery';\n\n/**\n * Write 52 ARKit blendshapes to morph target influences.\n * Uses pre-computed index arrays for zero-lookup hot path.\n *\n * @param blendshapes - Float32Array of 52 ARKit blendshape weights\n * @param morphEntries - Pre-computed morph index entries from discoverScene()\n */\nexport function writeBlendshapes(\n blendshapes: Float32Array,\n morphEntries: MorphIndexEntry[],\n): void {\n for (let e = 0; e < morphEntries.length; e++) {\n const { mesh, indices } = morphEntries[e];\n const influences = mesh.morphTargetInfluences;\n if (!influences) continue;\n for (let i = 0; i < 52; i++) {\n const morphIdx = indices[i];\n if (morphIdx >= 0) {\n influences[morphIdx] = blendshapes[i];\n }\n }\n }\n}\n","import { LAM_BLENDSHAPES, lerpBlendshapes } from '@omote/core';\nimport type { Object3D, SkinnedMesh } from 'three';\n\nexport interface BlendshapeControllerOptions {\n /** Blendshape names in order (default: LAM_BLENDSHAPES, 52 ARKit) */\n names?: readonly string[];\n /** Smoothing factor 0-1 (0 = no change, 1 = snap to target). Default: 0.7 */\n smoothing?: number;\n /** Traverse target for SkinnedMesh children automatically. Default: true */\n autoFind?: boolean;\n /** Called when meshes with morph targets are found */\n onMeshesFound?: (meshes: SkinnedMesh[]) => void;\n}\n\nexport class BlendshapeController {\n private _meshes: SkinnedMesh[] = [];\n private nameToIndex: Map<string, number>[] = [];\n private currentWeights: number[] = [];\n private blendshapeNames: readonly string[];\n private smoothing: number;\n private onMeshesFound?: (meshes: SkinnedMesh[]) => void;\n\n constructor(target: Object3D, options?: BlendshapeControllerOptions) {\n this.blendshapeNames = options?.names ?? LAM_BLENDSHAPES;\n this.smoothing = options?.smoothing ?? 0.7;\n this.onMeshesFound = options?.onMeshesFound;\n if (options?.autoFind !== false) {\n this.setTarget(target);\n }\n }\n\n get meshes(): SkinnedMesh[] {\n return this._meshes;\n }\n\n setTarget(target: Object3D): void {\n this._meshes = [];\n this.nameToIndex = [];\n\n target.traverse((child) => {\n const mesh = child as SkinnedMesh;\n if (mesh.morphTargetInfluences && mesh.morphTargetDictionary) {\n this._meshes.push(mesh);\n\n const map = new Map<string, number>();\n for (let i = 0; i < this.blendshapeNames.length; i++) {\n const name = this.blendshapeNames[i];\n const idx = mesh.morphTargetDictionary[name];\n if (idx !== undefined) {\n map.set(name, idx);\n }\n }\n this.nameToIndex.push(map);\n }\n });\n\n this.currentWeights = new Array(this.blendshapeNames.length).fill(0);\n\n if (this._meshes.length > 0 && this.onMeshesFound) {\n this.onMeshesFound(this._meshes);\n }\n }\n\n update(weights: Float32Array | number[]): void {\n this.currentWeights = lerpBlendshapes(this.currentWeights, weights, this.smoothing);\n\n for (let m = 0; m < this._meshes.length; m++) {\n const mesh = this._meshes[m];\n const map = this.nameToIndex[m];\n if (!mesh.morphTargetInfluences || !map) continue;\n\n for (let i = 0; i < this.blendshapeNames.length; i++) {\n const name = this.blendshapeNames[i];\n const dictIdx = map.get(name);\n if (dictIdx !== undefined) {\n mesh.morphTargetInfluences[dictIdx] = this.currentWeights[i];\n }\n }\n }\n }\n\n dispose(): void {\n this._meshes = [];\n this.nameToIndex = [];\n this.currentWeights = [];\n }\n}\n"],"mappings":"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","names":["import_core","logger","import_core"]}
|