@omote/avatar 0.1.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/dist/index.cjs +288 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +104 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.js +269 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
OmoteAvatarCore: () => OmoteAvatarCore
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/OmoteAvatarCore.ts
|
|
28
|
+
var import_core = require("@omote/core");
|
|
29
|
+
var OmoteAvatarCore = class {
|
|
30
|
+
constructor() {
|
|
31
|
+
// Voice primitives
|
|
32
|
+
this.ttsSpeaker = null;
|
|
33
|
+
this.speechListener = null;
|
|
34
|
+
this.voiceOrchestrator = null;
|
|
35
|
+
// Frame source wiring
|
|
36
|
+
this.frameSourceUnsub = null;
|
|
37
|
+
this.stateUnsub = null;
|
|
38
|
+
// Callbacks set by the adapter
|
|
39
|
+
this._onFrame = null;
|
|
40
|
+
this._onStateChange = null;
|
|
41
|
+
// State
|
|
42
|
+
this._isSpeaking = false;
|
|
43
|
+
this._state = "idle";
|
|
44
|
+
}
|
|
45
|
+
get isSpeaking() {
|
|
46
|
+
return this._isSpeaking;
|
|
47
|
+
}
|
|
48
|
+
get state() {
|
|
49
|
+
return this._state;
|
|
50
|
+
}
|
|
51
|
+
get speaker() {
|
|
52
|
+
return this.ttsSpeaker ?? this.voiceOrchestrator?.speaker ?? null;
|
|
53
|
+
}
|
|
54
|
+
get listener() {
|
|
55
|
+
return this.speechListener ?? this.voiceOrchestrator?.listener ?? null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Set the frame handler. Called by the adapter to receive blendshape frames.
|
|
59
|
+
* The adapter writes these to its renderer (morphTargets, refs, etc.).
|
|
60
|
+
*/
|
|
61
|
+
set onFrame(handler) {
|
|
62
|
+
this._onFrame = handler;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Set the state change handler. Called by the adapter to sync conversational state.
|
|
66
|
+
*/
|
|
67
|
+
set onStateChange(handler) {
|
|
68
|
+
this._onStateChange = handler;
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Speaker (TTS → lip sync)
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/** Warm up AudioContext for iOS/Safari autoplay policy. Call from user gesture. */
|
|
74
|
+
async warmup() {
|
|
75
|
+
if (this.ttsSpeaker) await this.ttsSpeaker.warmup();
|
|
76
|
+
}
|
|
77
|
+
async connectSpeaker(tts, config) {
|
|
78
|
+
await this.disconnectSpeaker();
|
|
79
|
+
const speaker = new import_core.TTSSpeaker();
|
|
80
|
+
await speaker.connect(tts, config);
|
|
81
|
+
this.ttsSpeaker = speaker;
|
|
82
|
+
this.wireSpeakerFrameSource(speaker);
|
|
83
|
+
}
|
|
84
|
+
async disconnectSpeaker() {
|
|
85
|
+
this.frameSourceUnsub?.();
|
|
86
|
+
this.frameSourceUnsub = null;
|
|
87
|
+
if (this.ttsSpeaker) {
|
|
88
|
+
await this.ttsSpeaker.dispose();
|
|
89
|
+
this.ttsSpeaker = null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async speak(text, options) {
|
|
93
|
+
if (this.voiceOrchestrator) {
|
|
94
|
+
await this.voiceOrchestrator.speak(text, options);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!this.ttsSpeaker) throw new Error("No speaker connected. Call connectSpeaker() first.");
|
|
98
|
+
this._isSpeaking = true;
|
|
99
|
+
this.updateState("speaking");
|
|
100
|
+
try {
|
|
101
|
+
await this.ttsSpeaker.speak(text, options);
|
|
102
|
+
} finally {
|
|
103
|
+
this._isSpeaking = false;
|
|
104
|
+
if (this._state === "speaking") this.updateState("idle");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async streamText(options) {
|
|
108
|
+
if (this.voiceOrchestrator) {
|
|
109
|
+
return this.voiceOrchestrator.streamText(options);
|
|
110
|
+
}
|
|
111
|
+
if (!this.ttsSpeaker) throw new Error("No speaker connected. Call connectSpeaker() first.");
|
|
112
|
+
this._isSpeaking = true;
|
|
113
|
+
this.updateState("speaking");
|
|
114
|
+
const stream = await this.ttsSpeaker.streamText(options ?? {});
|
|
115
|
+
return {
|
|
116
|
+
push: stream.push,
|
|
117
|
+
end: async () => {
|
|
118
|
+
try {
|
|
119
|
+
await stream.end();
|
|
120
|
+
} finally {
|
|
121
|
+
this._isSpeaking = false;
|
|
122
|
+
if (this._state === "speaking") this.updateState("idle");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
stopSpeaking() {
|
|
128
|
+
if (this.voiceOrchestrator) {
|
|
129
|
+
this.voiceOrchestrator.stopSpeaking();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this.ttsSpeaker?.stop();
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Listener (mic → VAD → ASR → transcript)
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
async connectListener(config) {
|
|
138
|
+
if (this.speechListener) {
|
|
139
|
+
await this.speechListener.dispose();
|
|
140
|
+
}
|
|
141
|
+
const listener = new import_core.SpeechListener(config);
|
|
142
|
+
this.speechListener = listener;
|
|
143
|
+
await listener.loadModels();
|
|
144
|
+
}
|
|
145
|
+
async disconnectListener() {
|
|
146
|
+
if (this.speechListener) {
|
|
147
|
+
await this.speechListener.dispose();
|
|
148
|
+
this.speechListener = null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async startListening() {
|
|
152
|
+
if (this.voiceOrchestrator) {
|
|
153
|
+
await this.voiceOrchestrator.startListening();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (!this.speechListener) throw new Error("No listener connected. Call connectListener() first.");
|
|
157
|
+
this.updateState("listening");
|
|
158
|
+
await this.speechListener.start();
|
|
159
|
+
}
|
|
160
|
+
stopListening() {
|
|
161
|
+
if (this.voiceOrchestrator) {
|
|
162
|
+
this.voiceOrchestrator.stopListening();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.speechListener?.stop();
|
|
166
|
+
if (this._state === "listening") this.updateState("idle");
|
|
167
|
+
}
|
|
168
|
+
// Event subscriptions (require listener or orchestrator)
|
|
169
|
+
onTranscript(callback) {
|
|
170
|
+
const listener = this.speechListener ?? this.voiceOrchestrator?.listener;
|
|
171
|
+
if (!listener) throw new Error("No listener connected. Call connectListener() or connectVoice() first.");
|
|
172
|
+
listener.on("transcript", callback);
|
|
173
|
+
return () => {
|
|
174
|
+
listener.off?.("transcript", callback);
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
onTranscriptEvent(callback) {
|
|
178
|
+
const orch = this.voiceOrchestrator;
|
|
179
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
180
|
+
return orch.on("transcript", callback);
|
|
181
|
+
}
|
|
182
|
+
onVoiceStateChange(callback) {
|
|
183
|
+
const orch = this.voiceOrchestrator;
|
|
184
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
185
|
+
return orch.on("state", callback);
|
|
186
|
+
}
|
|
187
|
+
onLoadingProgress(callback) {
|
|
188
|
+
const orch = this.voiceOrchestrator;
|
|
189
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
190
|
+
return orch.on("loading:progress", callback);
|
|
191
|
+
}
|
|
192
|
+
onError(callback) {
|
|
193
|
+
const orch = this.voiceOrchestrator;
|
|
194
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
195
|
+
return orch.on("error", callback);
|
|
196
|
+
}
|
|
197
|
+
onAudioLevel(callback) {
|
|
198
|
+
const orch = this.voiceOrchestrator;
|
|
199
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
200
|
+
return orch.on("audio:level", callback);
|
|
201
|
+
}
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Voice (combined speaker + listener + interruption)
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
async connectVoice(config) {
|
|
206
|
+
await this.disconnectVoice();
|
|
207
|
+
const orchestrator = new import_core.VoiceOrchestrator();
|
|
208
|
+
this.voiceOrchestrator = orchestrator;
|
|
209
|
+
await orchestrator.connect(config);
|
|
210
|
+
if (orchestrator.frameSource) {
|
|
211
|
+
this.wireFrameSource(orchestrator.frameSource);
|
|
212
|
+
}
|
|
213
|
+
this.stateUnsub = orchestrator.on("state", (state) => {
|
|
214
|
+
this._state = state;
|
|
215
|
+
this._isSpeaking = state === "speaking";
|
|
216
|
+
this._onStateChange?.(state);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
async disconnectVoice() {
|
|
220
|
+
if (this.voiceOrchestrator) {
|
|
221
|
+
this.frameSourceUnsub?.();
|
|
222
|
+
this.frameSourceUnsub = null;
|
|
223
|
+
this.stateUnsub?.();
|
|
224
|
+
this.stateUnsub = null;
|
|
225
|
+
await this.voiceOrchestrator.disconnect();
|
|
226
|
+
this.voiceOrchestrator = null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// Frame source
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
connectFrameSource(source) {
|
|
233
|
+
this.disconnectFrameSource();
|
|
234
|
+
this.wireFrameSource(source);
|
|
235
|
+
}
|
|
236
|
+
disconnectFrameSource() {
|
|
237
|
+
this.frameSourceUnsub?.();
|
|
238
|
+
this.frameSourceUnsub = null;
|
|
239
|
+
}
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// State helpers
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
setState(state) {
|
|
244
|
+
this._state = state;
|
|
245
|
+
this._onStateChange?.(state);
|
|
246
|
+
}
|
|
247
|
+
setSpeaking(speaking) {
|
|
248
|
+
this._isSpeaking = speaking;
|
|
249
|
+
}
|
|
250
|
+
reset() {
|
|
251
|
+
this._isSpeaking = false;
|
|
252
|
+
this._state = "idle";
|
|
253
|
+
this._onStateChange?.("idle");
|
|
254
|
+
}
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Cleanup
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
async dispose() {
|
|
259
|
+
await this.disconnectVoice();
|
|
260
|
+
await this.disconnectSpeaker();
|
|
261
|
+
await this.disconnectListener();
|
|
262
|
+
this._onFrame = null;
|
|
263
|
+
this._onStateChange = null;
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// Internal
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
wireFrameSource(source) {
|
|
269
|
+
const handler = (frame) => {
|
|
270
|
+
this._onFrame?.(frame);
|
|
271
|
+
};
|
|
272
|
+
source.on("frame", handler);
|
|
273
|
+
this.frameSourceUnsub = () => {
|
|
274
|
+
source.off?.("frame", handler);
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
wireSpeakerFrameSource(speaker) {
|
|
278
|
+
const source = speaker.frameSource;
|
|
279
|
+
if (source) {
|
|
280
|
+
this.wireFrameSource(source);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
updateState(state) {
|
|
284
|
+
this._state = state;
|
|
285
|
+
this._onStateChange?.(state);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/OmoteAvatarCore.ts"],"sourcesContent":["export { OmoteAvatarCore } from './OmoteAvatarCore';\nexport type { SpeakOptions, StreamTextSink, FrameHandler, StateChangeHandler } from './types';\n","/**\n * OmoteAvatarCore — Renderer-agnostic voice composition for OmoteAvatar adapters.\n *\n * Holds all voice state (TTSSpeaker, SpeechListener, VoiceOrchestrator) and\n * the imperative voice API. Adapters instantiate this and delegate voice\n * methods, keeping only renderer-specific code (animation, blendshape writes).\n *\n * Eliminates ~150 lines of identical voice wiring duplicated across\n * Three.js, Babylon.js, and R3F adapters.\n *\n * @category Avatar\n */\n\nimport {\n TTSSpeaker,\n SpeechListener,\n VoiceOrchestrator,\n} from '@omote/core';\nimport type {\n TTSBackend,\n TTSSpeakerConfig,\n SpeechListenerConfig,\n TranscriptResult,\n LoadingProgress,\n ConversationalState,\n FrameSource,\n VoiceOrchestratorConfig,\n} from '@omote/core';\nimport type { SpeakOptions, StreamTextSink, FrameHandler, StateChangeHandler } from './types';\n\n/**\n * Renderer-agnostic voice composition class.\n *\n * Owns: TTSSpeaker, SpeechListener, VoiceOrchestrator, frame source wiring.\n * Does NOT own: CharacterController, animation, scene graph, morph targets.\n */\nexport class OmoteAvatarCore {\n // Voice primitives\n private ttsSpeaker: TTSSpeaker | null = null;\n private speechListener: SpeechListener | null = null;\n private voiceOrchestrator: VoiceOrchestrator | null = null;\n\n // Frame source wiring\n private frameSourceUnsub: (() => void) | null = null;\n private stateUnsub: (() => void) | null = null;\n\n // Callbacks set by the adapter\n private _onFrame: FrameHandler | null = null;\n private _onStateChange: StateChangeHandler | null = null;\n\n // State\n private _isSpeaking = false;\n private _state: ConversationalState = 'idle';\n\n get isSpeaking(): boolean { return this._isSpeaking; }\n get state(): ConversationalState { return this._state; }\n get speaker(): TTSSpeaker | null { return this.ttsSpeaker ?? this.voiceOrchestrator?.speaker ?? null; }\n get listener(): SpeechListener | null { return this.speechListener ?? this.voiceOrchestrator?.listener ?? null; }\n\n /**\n * Set the frame handler. Called by the adapter to receive blendshape frames.\n * The adapter writes these to its renderer (morphTargets, refs, etc.).\n */\n set onFrame(handler: FrameHandler | null) { this._onFrame = handler; }\n\n /**\n * Set the state change handler. Called by the adapter to sync conversational state.\n */\n set onStateChange(handler: StateChangeHandler | null) { this._onStateChange = handler; }\n\n // ---------------------------------------------------------------------------\n // Speaker (TTS → lip sync)\n // ---------------------------------------------------------------------------\n\n /** Warm up AudioContext for iOS/Safari autoplay policy. Call from user gesture. */\n async warmup(): Promise<void> {\n if (this.ttsSpeaker) await this.ttsSpeaker.warmup();\n }\n\n async connectSpeaker(tts: TTSBackend, config?: TTSSpeakerConfig): Promise<void> {\n await this.disconnectSpeaker();\n const speaker = new TTSSpeaker();\n await speaker.connect(tts, config);\n this.ttsSpeaker = speaker;\n this.wireSpeakerFrameSource(speaker);\n }\n\n async disconnectSpeaker(): Promise<void> {\n this.frameSourceUnsub?.();\n this.frameSourceUnsub = null;\n if (this.ttsSpeaker) {\n await this.ttsSpeaker.dispose();\n this.ttsSpeaker = null;\n }\n }\n\n async speak(text: string, options?: SpeakOptions): Promise<void> {\n if (this.voiceOrchestrator) {\n await this.voiceOrchestrator.speak(text, options);\n return;\n }\n if (!this.ttsSpeaker) throw new Error('No speaker connected. Call connectSpeaker() first.');\n this._isSpeaking = true;\n this.updateState('speaking');\n try {\n await this.ttsSpeaker.speak(text, options);\n } finally {\n this._isSpeaking = false;\n if (this._state === 'speaking') this.updateState('idle');\n }\n }\n\n async streamText(options?: SpeakOptions): Promise<StreamTextSink> {\n if (this.voiceOrchestrator) {\n return this.voiceOrchestrator.streamText(options);\n }\n if (!this.ttsSpeaker) throw new Error('No speaker connected. Call connectSpeaker() first.');\n this._isSpeaking = true;\n this.updateState('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 {\n this._isSpeaking = false;\n if (this._state === 'speaking') this.updateState('idle');\n }\n },\n };\n }\n\n stopSpeaking(): void {\n if (this.voiceOrchestrator) {\n this.voiceOrchestrator.stopSpeaking();\n return;\n }\n this.ttsSpeaker?.stop();\n }\n\n // ---------------------------------------------------------------------------\n // Listener (mic → VAD → ASR → transcript)\n // ---------------------------------------------------------------------------\n\n async connectListener(config?: SpeechListenerConfig): Promise<void> {\n if (this.speechListener) {\n await this.speechListener.dispose();\n }\n const listener = new SpeechListener(config);\n this.speechListener = listener;\n await listener.loadModels();\n }\n\n async disconnectListener(): Promise<void> {\n if (this.speechListener) {\n await this.speechListener.dispose();\n this.speechListener = null;\n }\n }\n\n async startListening(): Promise<void> {\n if (this.voiceOrchestrator) {\n await this.voiceOrchestrator.startListening();\n return;\n }\n if (!this.speechListener) throw new Error('No listener connected. Call connectListener() first.');\n this.updateState('listening');\n await this.speechListener.start();\n }\n\n stopListening(): void {\n if (this.voiceOrchestrator) {\n this.voiceOrchestrator.stopListening();\n return;\n }\n this.speechListener?.stop();\n if (this._state === 'listening') this.updateState('idle');\n }\n\n // Event subscriptions (require listener or orchestrator)\n onTranscript(callback: (result: TranscriptResult) => void): () => void {\n const listener = this.speechListener ?? this.voiceOrchestrator?.listener;\n if (!listener) throw new Error('No listener connected. Call connectListener() or connectVoice() first.');\n listener.on('transcript', callback);\n return () => { listener.off?.('transcript', callback); };\n }\n\n onTranscriptEvent(callback: (result: TranscriptResult) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('transcript', callback);\n }\n\n onVoiceStateChange(callback: (state: ConversationalState) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('state', callback);\n }\n\n onLoadingProgress(callback: (progress: LoadingProgress) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('loading:progress', callback);\n }\n\n onError(callback: (error: Error) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('error', callback);\n }\n\n onAudioLevel(callback: (level: { rms: number; peak: number }) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('audio:level', callback);\n }\n\n // ---------------------------------------------------------------------------\n // Voice (combined speaker + listener + interruption)\n // ---------------------------------------------------------------------------\n\n async connectVoice(config: VoiceOrchestratorConfig): Promise<void> {\n await this.disconnectVoice();\n const orchestrator = new VoiceOrchestrator();\n this.voiceOrchestrator = orchestrator;\n await orchestrator.connect(config);\n\n // Wire frame source from orchestrator\n if (orchestrator.frameSource) {\n this.wireFrameSource(orchestrator.frameSource);\n }\n\n // Sync state from orchestrator\n this.stateUnsub = orchestrator.on('state', (state) => {\n this._state = state;\n this._isSpeaking = state === 'speaking';\n this._onStateChange?.(state);\n });\n }\n\n async disconnectVoice(): Promise<void> {\n if (this.voiceOrchestrator) {\n this.frameSourceUnsub?.();\n this.frameSourceUnsub = null;\n this.stateUnsub?.();\n this.stateUnsub = null;\n await this.voiceOrchestrator.disconnect();\n this.voiceOrchestrator = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Frame source\n // ---------------------------------------------------------------------------\n\n connectFrameSource(source: FrameSource): void {\n this.disconnectFrameSource();\n this.wireFrameSource(source);\n }\n\n disconnectFrameSource(): void {\n this.frameSourceUnsub?.();\n this.frameSourceUnsub = null;\n }\n\n // ---------------------------------------------------------------------------\n // State helpers\n // ---------------------------------------------------------------------------\n\n setState(state: ConversationalState): void {\n this._state = state;\n this._onStateChange?.(state);\n }\n\n setSpeaking(speaking: boolean): void {\n this._isSpeaking = speaking;\n }\n\n reset(): void {\n this._isSpeaking = false;\n this._state = 'idle';\n this._onStateChange?.('idle');\n }\n\n // ---------------------------------------------------------------------------\n // Cleanup\n // ---------------------------------------------------------------------------\n\n async dispose(): Promise<void> {\n await this.disconnectVoice();\n await this.disconnectSpeaker();\n await this.disconnectListener();\n this._onFrame = null;\n this._onStateChange = null;\n }\n\n // ---------------------------------------------------------------------------\n // Internal\n // ---------------------------------------------------------------------------\n\n private wireFrameSource(source: FrameSource): void {\n const handler = (frame: { blendshapes: Float32Array; emotion?: string }) => {\n this._onFrame?.(frame);\n };\n source.on('frame', handler);\n this.frameSourceUnsub = () => { source.off?.('frame', handler); };\n }\n\n private wireSpeakerFrameSource(speaker: TTSSpeaker): void {\n const source = speaker.frameSource;\n if (source) {\n this.wireFrameSource(source);\n }\n }\n\n private updateState(state: ConversationalState): void {\n this._state = state;\n this._onStateChange?.(state);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ConversationalState, TTSSpeaker, SpeechListener, TTSBackend, TTSSpeakerConfig, SpeechListenerConfig, TranscriptResult, LoadingProgress, VoiceOrchestratorConfig, FrameSource } from '@omote/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Types for @omote/avatar — renderer-agnostic voice composition.
|
|
5
|
+
*
|
|
6
|
+
* @category Avatar
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Options passed to `speak()` and `streamText()`. */
|
|
10
|
+
interface SpeakOptions {
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
voice?: string;
|
|
13
|
+
speed?: number;
|
|
14
|
+
language?: string;
|
|
15
|
+
}
|
|
16
|
+
/** Sink returned by `streamText()` for token-by-token streaming TTS. */
|
|
17
|
+
interface StreamTextSink {
|
|
18
|
+
push: (token: string) => void;
|
|
19
|
+
end: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/** Callback receiving blendshape frames from a FrameSource. */
|
|
22
|
+
type FrameHandler = (frame: {
|
|
23
|
+
blendshapes: Float32Array;
|
|
24
|
+
emotion?: string;
|
|
25
|
+
}) => void;
|
|
26
|
+
/** Callback receiving conversational state changes. */
|
|
27
|
+
type StateChangeHandler = (state: ConversationalState) => void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* OmoteAvatarCore — Renderer-agnostic voice composition for OmoteAvatar adapters.
|
|
31
|
+
*
|
|
32
|
+
* Holds all voice state (TTSSpeaker, SpeechListener, VoiceOrchestrator) and
|
|
33
|
+
* the imperative voice API. Adapters instantiate this and delegate voice
|
|
34
|
+
* methods, keeping only renderer-specific code (animation, blendshape writes).
|
|
35
|
+
*
|
|
36
|
+
* Eliminates ~150 lines of identical voice wiring duplicated across
|
|
37
|
+
* Three.js, Babylon.js, and R3F adapters.
|
|
38
|
+
*
|
|
39
|
+
* @category Avatar
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Renderer-agnostic voice composition class.
|
|
44
|
+
*
|
|
45
|
+
* Owns: TTSSpeaker, SpeechListener, VoiceOrchestrator, frame source wiring.
|
|
46
|
+
* Does NOT own: CharacterController, animation, scene graph, morph targets.
|
|
47
|
+
*/
|
|
48
|
+
declare class OmoteAvatarCore {
|
|
49
|
+
private ttsSpeaker;
|
|
50
|
+
private speechListener;
|
|
51
|
+
private voiceOrchestrator;
|
|
52
|
+
private frameSourceUnsub;
|
|
53
|
+
private stateUnsub;
|
|
54
|
+
private _onFrame;
|
|
55
|
+
private _onStateChange;
|
|
56
|
+
private _isSpeaking;
|
|
57
|
+
private _state;
|
|
58
|
+
get isSpeaking(): boolean;
|
|
59
|
+
get state(): ConversationalState;
|
|
60
|
+
get speaker(): TTSSpeaker | null;
|
|
61
|
+
get listener(): SpeechListener | null;
|
|
62
|
+
/**
|
|
63
|
+
* Set the frame handler. Called by the adapter to receive blendshape frames.
|
|
64
|
+
* The adapter writes these to its renderer (morphTargets, refs, etc.).
|
|
65
|
+
*/
|
|
66
|
+
set onFrame(handler: FrameHandler | null);
|
|
67
|
+
/**
|
|
68
|
+
* Set the state change handler. Called by the adapter to sync conversational state.
|
|
69
|
+
*/
|
|
70
|
+
set onStateChange(handler: StateChangeHandler | null);
|
|
71
|
+
/** Warm up AudioContext for iOS/Safari autoplay policy. Call from user gesture. */
|
|
72
|
+
warmup(): Promise<void>;
|
|
73
|
+
connectSpeaker(tts: TTSBackend, config?: TTSSpeakerConfig): Promise<void>;
|
|
74
|
+
disconnectSpeaker(): Promise<void>;
|
|
75
|
+
speak(text: string, options?: SpeakOptions): Promise<void>;
|
|
76
|
+
streamText(options?: SpeakOptions): Promise<StreamTextSink>;
|
|
77
|
+
stopSpeaking(): void;
|
|
78
|
+
connectListener(config?: SpeechListenerConfig): Promise<void>;
|
|
79
|
+
disconnectListener(): Promise<void>;
|
|
80
|
+
startListening(): Promise<void>;
|
|
81
|
+
stopListening(): void;
|
|
82
|
+
onTranscript(callback: (result: TranscriptResult) => void): () => void;
|
|
83
|
+
onTranscriptEvent(callback: (result: TranscriptResult) => void): () => void;
|
|
84
|
+
onVoiceStateChange(callback: (state: ConversationalState) => void): () => void;
|
|
85
|
+
onLoadingProgress(callback: (progress: LoadingProgress) => void): () => void;
|
|
86
|
+
onError(callback: (error: Error) => void): () => void;
|
|
87
|
+
onAudioLevel(callback: (level: {
|
|
88
|
+
rms: number;
|
|
89
|
+
peak: number;
|
|
90
|
+
}) => void): () => void;
|
|
91
|
+
connectVoice(config: VoiceOrchestratorConfig): Promise<void>;
|
|
92
|
+
disconnectVoice(): Promise<void>;
|
|
93
|
+
connectFrameSource(source: FrameSource): void;
|
|
94
|
+
disconnectFrameSource(): void;
|
|
95
|
+
setState(state: ConversationalState): void;
|
|
96
|
+
setSpeaking(speaking: boolean): void;
|
|
97
|
+
reset(): void;
|
|
98
|
+
dispose(): Promise<void>;
|
|
99
|
+
private wireFrameSource;
|
|
100
|
+
private wireSpeakerFrameSource;
|
|
101
|
+
private updateState;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { type FrameHandler, OmoteAvatarCore, type SpeakOptions, type StateChangeHandler, type StreamTextSink };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ConversationalState, TTSSpeaker, SpeechListener, TTSBackend, TTSSpeakerConfig, SpeechListenerConfig, TranscriptResult, LoadingProgress, VoiceOrchestratorConfig, FrameSource } from '@omote/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Types for @omote/avatar — renderer-agnostic voice composition.
|
|
5
|
+
*
|
|
6
|
+
* @category Avatar
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Options passed to `speak()` and `streamText()`. */
|
|
10
|
+
interface SpeakOptions {
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
voice?: string;
|
|
13
|
+
speed?: number;
|
|
14
|
+
language?: string;
|
|
15
|
+
}
|
|
16
|
+
/** Sink returned by `streamText()` for token-by-token streaming TTS. */
|
|
17
|
+
interface StreamTextSink {
|
|
18
|
+
push: (token: string) => void;
|
|
19
|
+
end: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/** Callback receiving blendshape frames from a FrameSource. */
|
|
22
|
+
type FrameHandler = (frame: {
|
|
23
|
+
blendshapes: Float32Array;
|
|
24
|
+
emotion?: string;
|
|
25
|
+
}) => void;
|
|
26
|
+
/** Callback receiving conversational state changes. */
|
|
27
|
+
type StateChangeHandler = (state: ConversationalState) => void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* OmoteAvatarCore — Renderer-agnostic voice composition for OmoteAvatar adapters.
|
|
31
|
+
*
|
|
32
|
+
* Holds all voice state (TTSSpeaker, SpeechListener, VoiceOrchestrator) and
|
|
33
|
+
* the imperative voice API. Adapters instantiate this and delegate voice
|
|
34
|
+
* methods, keeping only renderer-specific code (animation, blendshape writes).
|
|
35
|
+
*
|
|
36
|
+
* Eliminates ~150 lines of identical voice wiring duplicated across
|
|
37
|
+
* Three.js, Babylon.js, and R3F adapters.
|
|
38
|
+
*
|
|
39
|
+
* @category Avatar
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Renderer-agnostic voice composition class.
|
|
44
|
+
*
|
|
45
|
+
* Owns: TTSSpeaker, SpeechListener, VoiceOrchestrator, frame source wiring.
|
|
46
|
+
* Does NOT own: CharacterController, animation, scene graph, morph targets.
|
|
47
|
+
*/
|
|
48
|
+
declare class OmoteAvatarCore {
|
|
49
|
+
private ttsSpeaker;
|
|
50
|
+
private speechListener;
|
|
51
|
+
private voiceOrchestrator;
|
|
52
|
+
private frameSourceUnsub;
|
|
53
|
+
private stateUnsub;
|
|
54
|
+
private _onFrame;
|
|
55
|
+
private _onStateChange;
|
|
56
|
+
private _isSpeaking;
|
|
57
|
+
private _state;
|
|
58
|
+
get isSpeaking(): boolean;
|
|
59
|
+
get state(): ConversationalState;
|
|
60
|
+
get speaker(): TTSSpeaker | null;
|
|
61
|
+
get listener(): SpeechListener | null;
|
|
62
|
+
/**
|
|
63
|
+
* Set the frame handler. Called by the adapter to receive blendshape frames.
|
|
64
|
+
* The adapter writes these to its renderer (morphTargets, refs, etc.).
|
|
65
|
+
*/
|
|
66
|
+
set onFrame(handler: FrameHandler | null);
|
|
67
|
+
/**
|
|
68
|
+
* Set the state change handler. Called by the adapter to sync conversational state.
|
|
69
|
+
*/
|
|
70
|
+
set onStateChange(handler: StateChangeHandler | null);
|
|
71
|
+
/** Warm up AudioContext for iOS/Safari autoplay policy. Call from user gesture. */
|
|
72
|
+
warmup(): Promise<void>;
|
|
73
|
+
connectSpeaker(tts: TTSBackend, config?: TTSSpeakerConfig): Promise<void>;
|
|
74
|
+
disconnectSpeaker(): Promise<void>;
|
|
75
|
+
speak(text: string, options?: SpeakOptions): Promise<void>;
|
|
76
|
+
streamText(options?: SpeakOptions): Promise<StreamTextSink>;
|
|
77
|
+
stopSpeaking(): void;
|
|
78
|
+
connectListener(config?: SpeechListenerConfig): Promise<void>;
|
|
79
|
+
disconnectListener(): Promise<void>;
|
|
80
|
+
startListening(): Promise<void>;
|
|
81
|
+
stopListening(): void;
|
|
82
|
+
onTranscript(callback: (result: TranscriptResult) => void): () => void;
|
|
83
|
+
onTranscriptEvent(callback: (result: TranscriptResult) => void): () => void;
|
|
84
|
+
onVoiceStateChange(callback: (state: ConversationalState) => void): () => void;
|
|
85
|
+
onLoadingProgress(callback: (progress: LoadingProgress) => void): () => void;
|
|
86
|
+
onError(callback: (error: Error) => void): () => void;
|
|
87
|
+
onAudioLevel(callback: (level: {
|
|
88
|
+
rms: number;
|
|
89
|
+
peak: number;
|
|
90
|
+
}) => void): () => void;
|
|
91
|
+
connectVoice(config: VoiceOrchestratorConfig): Promise<void>;
|
|
92
|
+
disconnectVoice(): Promise<void>;
|
|
93
|
+
connectFrameSource(source: FrameSource): void;
|
|
94
|
+
disconnectFrameSource(): void;
|
|
95
|
+
setState(state: ConversationalState): void;
|
|
96
|
+
setSpeaking(speaking: boolean): void;
|
|
97
|
+
reset(): void;
|
|
98
|
+
dispose(): Promise<void>;
|
|
99
|
+
private wireFrameSource;
|
|
100
|
+
private wireSpeakerFrameSource;
|
|
101
|
+
private updateState;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { type FrameHandler, OmoteAvatarCore, type SpeakOptions, type StateChangeHandler, type StreamTextSink };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// src/OmoteAvatarCore.ts
|
|
2
|
+
import {
|
|
3
|
+
TTSSpeaker,
|
|
4
|
+
SpeechListener,
|
|
5
|
+
VoiceOrchestrator
|
|
6
|
+
} from "@omote/core";
|
|
7
|
+
var OmoteAvatarCore = class {
|
|
8
|
+
constructor() {
|
|
9
|
+
// Voice primitives
|
|
10
|
+
this.ttsSpeaker = null;
|
|
11
|
+
this.speechListener = null;
|
|
12
|
+
this.voiceOrchestrator = null;
|
|
13
|
+
// Frame source wiring
|
|
14
|
+
this.frameSourceUnsub = null;
|
|
15
|
+
this.stateUnsub = null;
|
|
16
|
+
// Callbacks set by the adapter
|
|
17
|
+
this._onFrame = null;
|
|
18
|
+
this._onStateChange = null;
|
|
19
|
+
// State
|
|
20
|
+
this._isSpeaking = false;
|
|
21
|
+
this._state = "idle";
|
|
22
|
+
}
|
|
23
|
+
get isSpeaking() {
|
|
24
|
+
return this._isSpeaking;
|
|
25
|
+
}
|
|
26
|
+
get state() {
|
|
27
|
+
return this._state;
|
|
28
|
+
}
|
|
29
|
+
get speaker() {
|
|
30
|
+
return this.ttsSpeaker ?? this.voiceOrchestrator?.speaker ?? null;
|
|
31
|
+
}
|
|
32
|
+
get listener() {
|
|
33
|
+
return this.speechListener ?? this.voiceOrchestrator?.listener ?? null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set the frame handler. Called by the adapter to receive blendshape frames.
|
|
37
|
+
* The adapter writes these to its renderer (morphTargets, refs, etc.).
|
|
38
|
+
*/
|
|
39
|
+
set onFrame(handler) {
|
|
40
|
+
this._onFrame = handler;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Set the state change handler. Called by the adapter to sync conversational state.
|
|
44
|
+
*/
|
|
45
|
+
set onStateChange(handler) {
|
|
46
|
+
this._onStateChange = handler;
|
|
47
|
+
}
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Speaker (TTS → lip sync)
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
/** Warm up AudioContext for iOS/Safari autoplay policy. Call from user gesture. */
|
|
52
|
+
async warmup() {
|
|
53
|
+
if (this.ttsSpeaker) await this.ttsSpeaker.warmup();
|
|
54
|
+
}
|
|
55
|
+
async connectSpeaker(tts, config) {
|
|
56
|
+
await this.disconnectSpeaker();
|
|
57
|
+
const speaker = new TTSSpeaker();
|
|
58
|
+
await speaker.connect(tts, config);
|
|
59
|
+
this.ttsSpeaker = speaker;
|
|
60
|
+
this.wireSpeakerFrameSource(speaker);
|
|
61
|
+
}
|
|
62
|
+
async disconnectSpeaker() {
|
|
63
|
+
this.frameSourceUnsub?.();
|
|
64
|
+
this.frameSourceUnsub = null;
|
|
65
|
+
if (this.ttsSpeaker) {
|
|
66
|
+
await this.ttsSpeaker.dispose();
|
|
67
|
+
this.ttsSpeaker = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async speak(text, options) {
|
|
71
|
+
if (this.voiceOrchestrator) {
|
|
72
|
+
await this.voiceOrchestrator.speak(text, options);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!this.ttsSpeaker) throw new Error("No speaker connected. Call connectSpeaker() first.");
|
|
76
|
+
this._isSpeaking = true;
|
|
77
|
+
this.updateState("speaking");
|
|
78
|
+
try {
|
|
79
|
+
await this.ttsSpeaker.speak(text, options);
|
|
80
|
+
} finally {
|
|
81
|
+
this._isSpeaking = false;
|
|
82
|
+
if (this._state === "speaking") this.updateState("idle");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async streamText(options) {
|
|
86
|
+
if (this.voiceOrchestrator) {
|
|
87
|
+
return this.voiceOrchestrator.streamText(options);
|
|
88
|
+
}
|
|
89
|
+
if (!this.ttsSpeaker) throw new Error("No speaker connected. Call connectSpeaker() first.");
|
|
90
|
+
this._isSpeaking = true;
|
|
91
|
+
this.updateState("speaking");
|
|
92
|
+
const stream = await this.ttsSpeaker.streamText(options ?? {});
|
|
93
|
+
return {
|
|
94
|
+
push: stream.push,
|
|
95
|
+
end: async () => {
|
|
96
|
+
try {
|
|
97
|
+
await stream.end();
|
|
98
|
+
} finally {
|
|
99
|
+
this._isSpeaking = false;
|
|
100
|
+
if (this._state === "speaking") this.updateState("idle");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
stopSpeaking() {
|
|
106
|
+
if (this.voiceOrchestrator) {
|
|
107
|
+
this.voiceOrchestrator.stopSpeaking();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.ttsSpeaker?.stop();
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Listener (mic → VAD → ASR → transcript)
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
async connectListener(config) {
|
|
116
|
+
if (this.speechListener) {
|
|
117
|
+
await this.speechListener.dispose();
|
|
118
|
+
}
|
|
119
|
+
const listener = new SpeechListener(config);
|
|
120
|
+
this.speechListener = listener;
|
|
121
|
+
await listener.loadModels();
|
|
122
|
+
}
|
|
123
|
+
async disconnectListener() {
|
|
124
|
+
if (this.speechListener) {
|
|
125
|
+
await this.speechListener.dispose();
|
|
126
|
+
this.speechListener = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async startListening() {
|
|
130
|
+
if (this.voiceOrchestrator) {
|
|
131
|
+
await this.voiceOrchestrator.startListening();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (!this.speechListener) throw new Error("No listener connected. Call connectListener() first.");
|
|
135
|
+
this.updateState("listening");
|
|
136
|
+
await this.speechListener.start();
|
|
137
|
+
}
|
|
138
|
+
stopListening() {
|
|
139
|
+
if (this.voiceOrchestrator) {
|
|
140
|
+
this.voiceOrchestrator.stopListening();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.speechListener?.stop();
|
|
144
|
+
if (this._state === "listening") this.updateState("idle");
|
|
145
|
+
}
|
|
146
|
+
// Event subscriptions (require listener or orchestrator)
|
|
147
|
+
onTranscript(callback) {
|
|
148
|
+
const listener = this.speechListener ?? this.voiceOrchestrator?.listener;
|
|
149
|
+
if (!listener) throw new Error("No listener connected. Call connectListener() or connectVoice() first.");
|
|
150
|
+
listener.on("transcript", callback);
|
|
151
|
+
return () => {
|
|
152
|
+
listener.off?.("transcript", callback);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
onTranscriptEvent(callback) {
|
|
156
|
+
const orch = this.voiceOrchestrator;
|
|
157
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
158
|
+
return orch.on("transcript", callback);
|
|
159
|
+
}
|
|
160
|
+
onVoiceStateChange(callback) {
|
|
161
|
+
const orch = this.voiceOrchestrator;
|
|
162
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
163
|
+
return orch.on("state", callback);
|
|
164
|
+
}
|
|
165
|
+
onLoadingProgress(callback) {
|
|
166
|
+
const orch = this.voiceOrchestrator;
|
|
167
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
168
|
+
return orch.on("loading:progress", callback);
|
|
169
|
+
}
|
|
170
|
+
onError(callback) {
|
|
171
|
+
const orch = this.voiceOrchestrator;
|
|
172
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
173
|
+
return orch.on("error", callback);
|
|
174
|
+
}
|
|
175
|
+
onAudioLevel(callback) {
|
|
176
|
+
const orch = this.voiceOrchestrator;
|
|
177
|
+
if (!orch) throw new Error("No voice connected. Call connectVoice() first.");
|
|
178
|
+
return orch.on("audio:level", callback);
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Voice (combined speaker + listener + interruption)
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
async connectVoice(config) {
|
|
184
|
+
await this.disconnectVoice();
|
|
185
|
+
const orchestrator = new VoiceOrchestrator();
|
|
186
|
+
this.voiceOrchestrator = orchestrator;
|
|
187
|
+
await orchestrator.connect(config);
|
|
188
|
+
if (orchestrator.frameSource) {
|
|
189
|
+
this.wireFrameSource(orchestrator.frameSource);
|
|
190
|
+
}
|
|
191
|
+
this.stateUnsub = orchestrator.on("state", (state) => {
|
|
192
|
+
this._state = state;
|
|
193
|
+
this._isSpeaking = state === "speaking";
|
|
194
|
+
this._onStateChange?.(state);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async disconnectVoice() {
|
|
198
|
+
if (this.voiceOrchestrator) {
|
|
199
|
+
this.frameSourceUnsub?.();
|
|
200
|
+
this.frameSourceUnsub = null;
|
|
201
|
+
this.stateUnsub?.();
|
|
202
|
+
this.stateUnsub = null;
|
|
203
|
+
await this.voiceOrchestrator.disconnect();
|
|
204
|
+
this.voiceOrchestrator = null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Frame source
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
connectFrameSource(source) {
|
|
211
|
+
this.disconnectFrameSource();
|
|
212
|
+
this.wireFrameSource(source);
|
|
213
|
+
}
|
|
214
|
+
disconnectFrameSource() {
|
|
215
|
+
this.frameSourceUnsub?.();
|
|
216
|
+
this.frameSourceUnsub = null;
|
|
217
|
+
}
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// State helpers
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
setState(state) {
|
|
222
|
+
this._state = state;
|
|
223
|
+
this._onStateChange?.(state);
|
|
224
|
+
}
|
|
225
|
+
setSpeaking(speaking) {
|
|
226
|
+
this._isSpeaking = speaking;
|
|
227
|
+
}
|
|
228
|
+
reset() {
|
|
229
|
+
this._isSpeaking = false;
|
|
230
|
+
this._state = "idle";
|
|
231
|
+
this._onStateChange?.("idle");
|
|
232
|
+
}
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
// Cleanup
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
async dispose() {
|
|
237
|
+
await this.disconnectVoice();
|
|
238
|
+
await this.disconnectSpeaker();
|
|
239
|
+
await this.disconnectListener();
|
|
240
|
+
this._onFrame = null;
|
|
241
|
+
this._onStateChange = null;
|
|
242
|
+
}
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// Internal
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
wireFrameSource(source) {
|
|
247
|
+
const handler = (frame) => {
|
|
248
|
+
this._onFrame?.(frame);
|
|
249
|
+
};
|
|
250
|
+
source.on("frame", handler);
|
|
251
|
+
this.frameSourceUnsub = () => {
|
|
252
|
+
source.off?.("frame", handler);
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
wireSpeakerFrameSource(speaker) {
|
|
256
|
+
const source = speaker.frameSource;
|
|
257
|
+
if (source) {
|
|
258
|
+
this.wireFrameSource(source);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
updateState(state) {
|
|
262
|
+
this._state = state;
|
|
263
|
+
this._onStateChange?.(state);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
export {
|
|
267
|
+
OmoteAvatarCore
|
|
268
|
+
};
|
|
269
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/OmoteAvatarCore.ts"],"sourcesContent":["/**\n * OmoteAvatarCore — Renderer-agnostic voice composition for OmoteAvatar adapters.\n *\n * Holds all voice state (TTSSpeaker, SpeechListener, VoiceOrchestrator) and\n * the imperative voice API. Adapters instantiate this and delegate voice\n * methods, keeping only renderer-specific code (animation, blendshape writes).\n *\n * Eliminates ~150 lines of identical voice wiring duplicated across\n * Three.js, Babylon.js, and R3F adapters.\n *\n * @category Avatar\n */\n\nimport {\n TTSSpeaker,\n SpeechListener,\n VoiceOrchestrator,\n} from '@omote/core';\nimport type {\n TTSBackend,\n TTSSpeakerConfig,\n SpeechListenerConfig,\n TranscriptResult,\n LoadingProgress,\n ConversationalState,\n FrameSource,\n VoiceOrchestratorConfig,\n} from '@omote/core';\nimport type { SpeakOptions, StreamTextSink, FrameHandler, StateChangeHandler } from './types';\n\n/**\n * Renderer-agnostic voice composition class.\n *\n * Owns: TTSSpeaker, SpeechListener, VoiceOrchestrator, frame source wiring.\n * Does NOT own: CharacterController, animation, scene graph, morph targets.\n */\nexport class OmoteAvatarCore {\n // Voice primitives\n private ttsSpeaker: TTSSpeaker | null = null;\n private speechListener: SpeechListener | null = null;\n private voiceOrchestrator: VoiceOrchestrator | null = null;\n\n // Frame source wiring\n private frameSourceUnsub: (() => void) | null = null;\n private stateUnsub: (() => void) | null = null;\n\n // Callbacks set by the adapter\n private _onFrame: FrameHandler | null = null;\n private _onStateChange: StateChangeHandler | null = null;\n\n // State\n private _isSpeaking = false;\n private _state: ConversationalState = 'idle';\n\n get isSpeaking(): boolean { return this._isSpeaking; }\n get state(): ConversationalState { return this._state; }\n get speaker(): TTSSpeaker | null { return this.ttsSpeaker ?? this.voiceOrchestrator?.speaker ?? null; }\n get listener(): SpeechListener | null { return this.speechListener ?? this.voiceOrchestrator?.listener ?? null; }\n\n /**\n * Set the frame handler. Called by the adapter to receive blendshape frames.\n * The adapter writes these to its renderer (morphTargets, refs, etc.).\n */\n set onFrame(handler: FrameHandler | null) { this._onFrame = handler; }\n\n /**\n * Set the state change handler. Called by the adapter to sync conversational state.\n */\n set onStateChange(handler: StateChangeHandler | null) { this._onStateChange = handler; }\n\n // ---------------------------------------------------------------------------\n // Speaker (TTS → lip sync)\n // ---------------------------------------------------------------------------\n\n /** Warm up AudioContext for iOS/Safari autoplay policy. Call from user gesture. */\n async warmup(): Promise<void> {\n if (this.ttsSpeaker) await this.ttsSpeaker.warmup();\n }\n\n async connectSpeaker(tts: TTSBackend, config?: TTSSpeakerConfig): Promise<void> {\n await this.disconnectSpeaker();\n const speaker = new TTSSpeaker();\n await speaker.connect(tts, config);\n this.ttsSpeaker = speaker;\n this.wireSpeakerFrameSource(speaker);\n }\n\n async disconnectSpeaker(): Promise<void> {\n this.frameSourceUnsub?.();\n this.frameSourceUnsub = null;\n if (this.ttsSpeaker) {\n await this.ttsSpeaker.dispose();\n this.ttsSpeaker = null;\n }\n }\n\n async speak(text: string, options?: SpeakOptions): Promise<void> {\n if (this.voiceOrchestrator) {\n await this.voiceOrchestrator.speak(text, options);\n return;\n }\n if (!this.ttsSpeaker) throw new Error('No speaker connected. Call connectSpeaker() first.');\n this._isSpeaking = true;\n this.updateState('speaking');\n try {\n await this.ttsSpeaker.speak(text, options);\n } finally {\n this._isSpeaking = false;\n if (this._state === 'speaking') this.updateState('idle');\n }\n }\n\n async streamText(options?: SpeakOptions): Promise<StreamTextSink> {\n if (this.voiceOrchestrator) {\n return this.voiceOrchestrator.streamText(options);\n }\n if (!this.ttsSpeaker) throw new Error('No speaker connected. Call connectSpeaker() first.');\n this._isSpeaking = true;\n this.updateState('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 {\n this._isSpeaking = false;\n if (this._state === 'speaking') this.updateState('idle');\n }\n },\n };\n }\n\n stopSpeaking(): void {\n if (this.voiceOrchestrator) {\n this.voiceOrchestrator.stopSpeaking();\n return;\n }\n this.ttsSpeaker?.stop();\n }\n\n // ---------------------------------------------------------------------------\n // Listener (mic → VAD → ASR → transcript)\n // ---------------------------------------------------------------------------\n\n async connectListener(config?: SpeechListenerConfig): Promise<void> {\n if (this.speechListener) {\n await this.speechListener.dispose();\n }\n const listener = new SpeechListener(config);\n this.speechListener = listener;\n await listener.loadModels();\n }\n\n async disconnectListener(): Promise<void> {\n if (this.speechListener) {\n await this.speechListener.dispose();\n this.speechListener = null;\n }\n }\n\n async startListening(): Promise<void> {\n if (this.voiceOrchestrator) {\n await this.voiceOrchestrator.startListening();\n return;\n }\n if (!this.speechListener) throw new Error('No listener connected. Call connectListener() first.');\n this.updateState('listening');\n await this.speechListener.start();\n }\n\n stopListening(): void {\n if (this.voiceOrchestrator) {\n this.voiceOrchestrator.stopListening();\n return;\n }\n this.speechListener?.stop();\n if (this._state === 'listening') this.updateState('idle');\n }\n\n // Event subscriptions (require listener or orchestrator)\n onTranscript(callback: (result: TranscriptResult) => void): () => void {\n const listener = this.speechListener ?? this.voiceOrchestrator?.listener;\n if (!listener) throw new Error('No listener connected. Call connectListener() or connectVoice() first.');\n listener.on('transcript', callback);\n return () => { listener.off?.('transcript', callback); };\n }\n\n onTranscriptEvent(callback: (result: TranscriptResult) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('transcript', callback);\n }\n\n onVoiceStateChange(callback: (state: ConversationalState) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('state', callback);\n }\n\n onLoadingProgress(callback: (progress: LoadingProgress) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('loading:progress', callback);\n }\n\n onError(callback: (error: Error) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('error', callback);\n }\n\n onAudioLevel(callback: (level: { rms: number; peak: number }) => void): () => void {\n const orch = this.voiceOrchestrator;\n if (!orch) throw new Error('No voice connected. Call connectVoice() first.');\n return orch.on('audio:level', callback);\n }\n\n // ---------------------------------------------------------------------------\n // Voice (combined speaker + listener + interruption)\n // ---------------------------------------------------------------------------\n\n async connectVoice(config: VoiceOrchestratorConfig): Promise<void> {\n await this.disconnectVoice();\n const orchestrator = new VoiceOrchestrator();\n this.voiceOrchestrator = orchestrator;\n await orchestrator.connect(config);\n\n // Wire frame source from orchestrator\n if (orchestrator.frameSource) {\n this.wireFrameSource(orchestrator.frameSource);\n }\n\n // Sync state from orchestrator\n this.stateUnsub = orchestrator.on('state', (state) => {\n this._state = state;\n this._isSpeaking = state === 'speaking';\n this._onStateChange?.(state);\n });\n }\n\n async disconnectVoice(): Promise<void> {\n if (this.voiceOrchestrator) {\n this.frameSourceUnsub?.();\n this.frameSourceUnsub = null;\n this.stateUnsub?.();\n this.stateUnsub = null;\n await this.voiceOrchestrator.disconnect();\n this.voiceOrchestrator = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Frame source\n // ---------------------------------------------------------------------------\n\n connectFrameSource(source: FrameSource): void {\n this.disconnectFrameSource();\n this.wireFrameSource(source);\n }\n\n disconnectFrameSource(): void {\n this.frameSourceUnsub?.();\n this.frameSourceUnsub = null;\n }\n\n // ---------------------------------------------------------------------------\n // State helpers\n // ---------------------------------------------------------------------------\n\n setState(state: ConversationalState): void {\n this._state = state;\n this._onStateChange?.(state);\n }\n\n setSpeaking(speaking: boolean): void {\n this._isSpeaking = speaking;\n }\n\n reset(): void {\n this._isSpeaking = false;\n this._state = 'idle';\n this._onStateChange?.('idle');\n }\n\n // ---------------------------------------------------------------------------\n // Cleanup\n // ---------------------------------------------------------------------------\n\n async dispose(): Promise<void> {\n await this.disconnectVoice();\n await this.disconnectSpeaker();\n await this.disconnectListener();\n this._onFrame = null;\n this._onStateChange = null;\n }\n\n // ---------------------------------------------------------------------------\n // Internal\n // ---------------------------------------------------------------------------\n\n private wireFrameSource(source: FrameSource): void {\n const handler = (frame: { blendshapes: Float32Array; emotion?: string }) => {\n this._onFrame?.(frame);\n };\n source.on('frame', handler);\n this.frameSourceUnsub = () => { source.off?.('frame', handler); };\n }\n\n private wireSpeakerFrameSource(speaker: TTSSpeaker): void {\n const source = speaker.frameSource;\n if (source) {\n this.wireFrameSource(source);\n }\n }\n\n private updateState(state: ConversationalState): void {\n this._state = state;\n this._onStateChange?.(state);\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@omote/avatar",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Renderer-agnostic avatar voice composition for Omote SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"typecheck": "tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"@omote/core": ">=0.10.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@omote/core": "file:../core",
|
|
25
|
+
"tsup": "^8.0.0",
|
|
26
|
+
"typescript": "^5.3.0"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/omoteai/omote.git",
|
|
38
|
+
"directory": "packages/avatar"
|
|
39
|
+
}
|
|
40
|
+
}
|